J'ai entendu auto_ptr
est déconseillé en C++ 11. Quelle est la raison pour ça?
J'aimerais aussi connaître la différence entre auto_ptr
et shared_ptr
.
Le remplacement direct de auto_ptr
(ou la chose la plus proche 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 des mouvements et à la magie des références rvalue, il peut le faire considérablement plus naturellement. Il "s'adapte" également beaucoup mieux au reste de la bibliothèque standard (bien que, pour être honnête, cela soit dû au fait que le reste de la bibliothèque change pour s'adapter à la sémantique de déplacement au lieu de toujours exiger la copie).
Le changement de nom est également (OMI) le bienvenu - auto_ptr
ne vous dit pas grand-chose sur ce qu'il essaie d'automatiser, tandis que unique_ptr
est une description assez raisonnable (si concise) de ce qui est fourni.
J'ai trouvé les réponses existantes excellentes, mais d'après le PoV des pointeurs. IMO, une réponse idéale devrait avoir la réponse en perspective de l'utilisateur/programmeur.
Première chose 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, alors aller avec shared_ptr.
Par DIFFERENT-Times, pensez à une situation où 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 concerne est de libérer de la mémoire, et l'accès à l'objet est SEQUENTIEL, 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 [~ # ~] d'abord [~ # ~] structure de données. Ensuite, l'objet est détruit après la structure de données ONE ou est déplacé vers [~ # ~] seconde [~ # ~] structure de données.
À 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 soulignerai dans les lignes suivantes, ils ne devraient pas être groupés avec pointeur intelligent.)
La raison la plus importante pour laquelle auto_ptr a été dépréciée en faveur du pointeur intelligent est la sémantique d'affectation Si ce n'était pas pour cette raison, ils auraient ajouté tous les nouveaux goodies de la sémantique de mouvement à l'auto_ptr au lieu de le déprécier. Étant donné que la sémantique d'affectation était la fonctionnalité la plus détestée, ils voulaient que cette fonctionnalité disparaisse, mais comme il existe un code écrit qui utilise cette sémantique, (que le comité de normes ne peut pas modifier), ils ont dû abandonner auto_ptr, au lieu de le modifier.
Sur le 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
Venons-en maintenant à la raison pour laquelle l'affectation de copie elle-même était si détesté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% des programmeurs qui souhaitent intentionnellement transférer la propriété, C++ 11 leur a donné std :: move (), ce qui a rendu leur intention claire pour tous les stagiaires qui vont lire et maintenir le code.)
shared_ptr
peut être stocké dans des conteneurs. auto_ptr
ne peut pas.
BTW unique_ptr
est vraiment le direct auto_ptr
remplacement, il combine les meilleures fonctionnalités des deux std::auto_ptr
et boost::scoped_ptr
.
Encore une autre façon d'expliquer la différence ...
Fonctionnellement, C++ 11's std::unique_ptr
est le "fixe" std::auto_ptr
: les deux conviennent quand - à tout moment de l'exécution - il devrait y avoir un seul propriétaire de pointeur intelligent pour un objet pointé.
La différence cruciale réside dans la construction de la copie ou l'affectation à partir d'un autre pointeur intelligent non expirant, indiqué sur le =>
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
Au dessus de, ap3
"vole" tranquillement la propriété de *ap
, laissant ap
réglé sur nullptr
, et le problème est que cela peut arriver trop facilement, sans que le programmeur ait réfléchi à sa sécurité.
Par exemple, si un class
/struct
a un std::auto_ptr
membre, puis faire une copie d'une instance release
le pointeur de l'instance en cours de copie: c'est une sémantique étrange et dangereusement confuse car généralement copier quelque chose ne le modifie pas. Il est facile pour l'auteur de la classe/structure d'ignorer la libération du pointeur lorsqu'il raisonne sur les invariants et l'état, et par conséquent, tente accidentellement de déréférencer le pointeur intelligent alors qu'il est nul, ou tout simplement ne pas toujours avoir accès/propriété des données pointées.
auto_ptr ne peut pas être utilisé dans les conteneurs STL car il a 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 des méthodes alternatives. 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