web-dev-qa-db-fra.com

Pourquoi auto_ptr est-il déconseillé?

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.

84
brett

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.

86
Jerry Coffin

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)

  • auto_ptr pourrait être remplacé par shared_ptr ou unique_ptr selon la situation

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

  • affectation de déplacement (1)
  • attribuer un pointeur nul (2)
  • affectation de type (3)
  • attribution de copie (supprimée!) (4)

De: http://www.cplusplus.com/reference/memory/auto_ptr/operator=/

Type d'affectations prises en charge par auto_ptr

  • assignation de copie (4) coupable

Venons-en maintenant à la raison pour laquelle l'affectation de copie elle-même était si détestée, j'ai cette théorie:

  1. Tous les programmeurs ne lisent pas des livres ou des normes
  2. auto_ptr à première vue, vous promet la propriété de l'objet
  3. la clause little- * (jeu de mots voulu) de l'auto_ptr, qui n'est pas lue par tous les programmeurs, autorise l'affectation d'un auto_ptr à un autre et transfère la propriété.
  4. La recherche a montré que ce comportement est destiné à 3,1415926535% de toute utilisation et involontaire dans d'autres cas.

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.)

34
Ajeet Ganga

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.

21
Ben Voigt

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.

11
Tony Delroy

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
3
edW