J'ai entendu auto_ptr
dire qu'il était obsolète dans C ++ 11. Quelle est la raison pour ça?
J'aimerais aussi connaître la différence entre auto_ptr
et shared_ptr
.
Réponses:
Le remplacement direct pour auto_ptr
(ou la chose la plus proche de l'une de toute façon) est unique_ptr
. En ce qui concerne le «problème», c'est assez simple: auto_ptr
transfère la propriété lorsqu'elle est attribuée. unique_ptr
transfère également la propriété, mais grâce à la codification de la sémantique de déplacement et à la magie des références rvalue, il peut le faire beaucoup plus naturellement. Il «s'adapte» également beaucoup mieux au reste de la bibliothèque standard (bien que, en toute honnêteté, cela soit dû en partie au changement du reste de la bibliothèque pour s'adapter à la sémantique de déplacement au lieu de toujours exiger la copie).
Le changement de nom est également le bienvenu (IMO) - auto_ptr
ne vous en dit pas vraiment beaucoup sur ce qu'il tente d'automatiser, alors qu'il unique_ptr
s'agit d'une description assez raisonnable (si laconique) de ce qui est fourni.
auto_ptr
nom: auto suggère automatique comme dans la variable automatique, et il se réfère à une chose qui auto_ptr
fait: détruire la ressource gérée dans son destructeur (quand elle est hors de portée).
auto_ptr
: open-std.org/jtc1/sc22/wg21/docs/papers/2005
std::sort
n'a pas de spécialisation pour unique_ptr
. Au lieu de cela, il a été redéfini pour ne jamais copier. Donc , en auto_ptr
réalité fait le travail avec le moderne sort
. Mais le C ++ 98/03 sort
n'est qu'un exemple d'algorithme ici: tout algorithme générique (fourni par std ou écrit par l'utilisateur) qui suppose que la syntaxe de copie a une sémantique de copie aura probablement une erreur d'exécution s'il est utilisé avec auto_ptr
, car se déplaceauto_ptr
silencieusement avec la syntaxe de copie . Le problème est bien plus vaste que juste . sort
J'ai trouvé les réponses existantes excellentes, mais à partir du PoV des pointeurs. OMI, une réponse idéale devrait avoir la réponse du point de vue de l'utilisateur / programmeur.
Tout d'abord (comme l'a souligné Jerry Coffin dans sa réponse)
shared_ptr: Si vous êtes préoccupé par la libération de ressources / mémoire ET si vous avez plus d'une fonction qui pourrait utiliser l'objet AT-DIFFERENT fois, optez pour shared_ptr.
Par DIFFERENT-Times, pensez à une situation dans laquelle l'objet-ptr est stocké dans plusieurs structures de données et accédé plus tard. Plusieurs threads, bien sûr, est un autre exemple.
unique_ptr: Si tout ce qui vous préoccupe est de libérer de la mémoire et que l'accès à l'objet est SEQUENTIAL, alors optez pour unique_ptr.
Par SÉQUENTIEL, je veux dire, à tout moment, un objet sera accessible à partir d'un contexte. Par exemple, un objet qui a été créé et utilisé immédiatement après sa création par le créateur. Après la création, l'objet est stocké dans la structure de données FIRST . Ensuite, soit l'objet est détruit après la structure de données ONE, soit est déplacé vers la structure de données SECOND .
À partir de cette ligne, je ferai référence à _ptr partagé / unique en tant que pointeurs intelligents. (auto_ptr est également un pointeur intelligent MAIS en raison de défauts dans sa conception, pour lesquels ils sont obsolètes, et que je pense que je vais souligner dans les prochaines lignes, ils ne doivent pas être regroupés avec un pointeur intelligent.)
La raison la plus importante pour laquelle auto_ptr était obsolète en faveur de smart-pointer est la sémantique d'affectation. Si ce n'était pas pour cette raison, ils auraient ajouté tous les nouveaux avantages de la sémantique de déplacement à auto_ptr au lieu de le déprécier. Comme la sémantique des affectations était la fonctionnalité la plus détestée, ils voulaient que cette fonctionnalité disparaisse, mais comme il y a du code écrit qui utilise cette sémantique, (que le comité des normes ne peut pas changer), ils ont dû abandonner auto_ptr, au lieu de le modifier.
À partir du lien: http://www.cplusplus.com/reference/memory/unique_ptr/operator=/
Type d'affectations prises en charge par unqiue_ptr
De: http://www.cplusplus.com/reference/memory/auto_ptr/operator=/
Type d'affectations prises en charge par auto_ptr
Venant maintenant à la raison pour laquelle l'affectation de copie elle-même n'a pas été si appréciée, j'ai cette théorie:
Le comportement involontaire est vraiment détesté et donc l'aversion pour l'auto_ptr.
(Pour les 3,1415926536% de programmeurs qui souhaitent intentionnellement transférer la propriété, C ++ 11 leur a donné std :: move (), ce qui a clairement indiqué leur intention à tous les stagiaires qui vont lire et maintenir le code.)
auto_ptr
valeurs pointent vers le même objet (puisqu'elles ne donnent pas la propriété partagée, le premier à mourir laissera l'autre avec un héritage mortel; cela est également vrai pour l' unique_ptr
usage), pouvez-vous suggérer ce qui était prévu dans ceux qui restent 96.8584073465% de toute l'utilisation?
*a=*b;
ici, seule la valeur de b est copiée dans a. J'espère que la propriété de a et de b appartient toujours aux mêmes personnes. Vous avez mentionné que la propriété sera transférée. Comment ça va être?
auto_ptr
objet lui-même. L'attribution de / à partir de sa valeur désignée n'a aucun effet sur la propriété ni aucune pertinence pour celle-ci. J'espère que vous ne l'utilisez pas encore auto_ptr
?
Encore une autre façon d'expliquer la différence ...
Fonctionnellement, C ++ 11 std::unique_ptr
est le "fixe" std::auto_ptr
: les deux conviennent quand - à tout moment pendant l'exécution - il devrait y avoir un seul propriétaire de pointeur intelligent pour un objet pointé vers.
La différence cruciale réside dans la construction de la copie ou l'affectation à partir d'un autre pointeur intelligent non expirant, illustré sur les =>
lignes ci-dessous:
std::auto_ptr<T> ap(...);
std::auto_ptr<T> ap2(get_ap_to_T()); // take expiring ownership
=> std::auto_ptr<T> ap3(ap); // take un-expiring ownership ala ap3(ap.release());
ap->xyz; // oops... can still try to use ap, expecting it to be non-NULL
std::unique_ptr<T> up(...);
std::unique_ptr<T> up2(get_up_to_T()); // take expiring ownership
=> std::unique_ptr<T> up3(up); // COMPILE ERROR: can't take un-expiring ownership
=> std::unique_ptr<T> up4(std::move(up)); // EXPLICIT code allowed
=> std::unique_ptr<T> up4(up.release()); // EXPLICIT code allowed
Ci-dessus, ap3
discrètement "vole" la propriété de *ap
, en laissant le ap
réglage à a nullptr
, et le problème est que cela peut arriver trop facilement, sans que le programmeur ait pensé à sa sécurité.
Par exemple, si un class
/ struct
a un std::auto_ptr
membre, alors faire une copie d'une instance sera release
le pointeur de l'instance en cours de copie: c'est une sémantique étrange et dangereusement déroutante car généralement copier quelque chose ne le modifie pas. Il est facile pour l'auteur de la classe / structure d'ignorer la publication du pointeur lors du raisonnement sur les invariants et l'état, et par conséquent de tenter accidentellement de déréférencer le pointeur intelligent alors qu'il est nul, ou tout simplement de ne pas avoir toujours l'accès / la propriété prévu des données pointées.
auto_ptr ne peut pas être utilisé dans les conteneurs STL car il possède un constructeur de copie qui ne répond pas aux exigences du conteneur CopyConstructible . unique_ptr n'implémente pas de constructeur de copie, les conteneurs utilisent donc d'autres méthodes. unique_ptr peut être utilisé dans des conteneurs et est plus rapide pour les algorithmes std que shared_ptr.
#include <iostream>
#include <type_traits>
#include <vector>
#include <memory>
using namespace std;
int main() {
cout << boolalpha;
cout << "is_copy_constructible:" << endl;
cout << "auto_ptr: " << is_copy_constructible< auto_ptr<int> >::value << endl;
cout << "unique_ptr: " << is_copy_constructible< unique_ptr<int> >::value << endl;
cout << "shared_ptr: " << is_copy_constructible< shared_ptr<int> >::value << endl;
vector<int> i_v;
i_v.push_back(1);
cout << "i_v=" << i_v[0] << endl;
vector<int> i_v2=i_v;
cout << "i_v2=" << i_v2[0] << endl;
vector< unique_ptr<int> > u_v;
u_v.push_back(unique_ptr<int>(new int(2)));
cout << "u_v=" << *u_v[0] << endl;
//vector< unique_ptr<int> > u_v2=u_v; //will not compile, need is_copy_constructible == true
vector< unique_ptr<int> > u_v2 =std::move(u_v); // but can be moved
cout << "u_v2=" << *u_v2[0] << " length u_v: " <<u_v.size() << endl;
vector< shared_ptr<int> > s_v;
shared_ptr<int> s(new int(3));
s_v.push_back(s);
cout << "s_v=" << *s_v[0] << endl;
vector< shared_ptr<int> > s_v2=s_v;
cout << "s_v2=" << *s_v2[0] << endl;
vector< auto_ptr<int> > a_v; //USAGE ERROR
return 0;
}
>cxx test1.cpp -o test1
test1.cpp: In function âint main()â:
test1.cpp:33:11: warning: âauto_ptrâ is deprecated (declared at /apps/hermes/sw/gcc/gcc-4.8.5/include/c++/4.8.5/backward/auto_ptr.h:87) [-Wdeprecated-declarations]
vector< auto_ptr<int> > a_v; //USAGE ERROR
^
>./test1
is_copy_constructible:
auto_ptr: false
unique_ptr: false
shared_ptr: true
i_v=1
i_v2=1
u_v=2
s_v=3
s_v2=3