Pourquoi auto_ptr est-il obsolète?

Réponses:

91

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_ptrtransfère la propriété lorsqu'elle est attribuée. unique_ptrtransfè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_ptrne vous en dit pas vraiment beaucoup sur ce qu'il tente d'automatiser, alors qu'il unique_ptrs'agit d'une description assez raisonnable (si laconique) de ce qui est fourni.

Jerry Coffin
la source
24
Juste une note sur le auto_ptrnom: auto suggère automatique comme dans la variable automatique, et il se réfère à une chose qui auto_ptrfait: détruire la ressource gérée dans son destructeur (quand elle est hors de portée).
Vincenzo Pii
13
Plus d'informations: Voici la justification officielle de la dépréciation auto_ptr: open-std.org/jtc1/sc22/wg21/docs/papers/2005
Howard Hinnant
@HowardHinnant intéressant doc! il est étrange dans un sens que si std :: sort () a une spécialisation pour std :: unique_ptr pour utiliser la sémantique de déplacement si nécessaire. Je me demande pourquoi std :: sort () ne peut pas être spécialisé pour std :: auto_ptr pour corriger le problème de copie mentionné dans la doc. Merci d'avance.
Hei
2
@Hei: std::sortn'a pas de spécialisation pour unique_ptr. Au lieu de cela, il a été redéfini pour ne jamais copier. Donc , en auto_ptrréalité fait le travail avec le moderne sort. Mais le C ++ 98/03 sortn'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
Howard Hinnant
35

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)

  • 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, 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

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

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

Type d'affectations prises en charge par auto_ptr

  • cession de copie (4) coupable

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:

  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 intentionnel) de l'auto_ptr, qui n'est pas lu par tous les programmeurs, autorise l'attribution d'un auto_ptr à un autre et transfère la propriété.
  4. Des recherches ont montré que ce comportement est destiné à 3,1415926535% de toute utilisation, et non intentionnel dans d'autres cas.

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

Ajeet Ganga
la source
1
Puisque vous ne voulez jamais que deux auto_ptrvaleurs 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_ptrusage), pouvez-vous suggérer ce qui était prévu dans ceux qui restent 96.8584073465% de toute l'utilisation?
Marc van Leeuwen
Je ne peux pas parler pour tous, mais je suppose qu'ils penseraient que la propriété de l'objet est déplacée et PAS simplement dupliquée, ce qui est erroné.
Ajeet Ganga
@AjeetGanga Dans la phrase suivante «le petit- * (jeu de mots intentionnel)», vous avez mentionné comme «jeu de mots intentionnel». Cette phrase est nouvelle pour moi et de toute façon je l'ai recherchée sur Google et j'ai appris qu'il y avait une blague qui a été délibérément faite ici. Quelle est cette blague ici? Juste curieux de le savoir.
VINOTH ENERGETIC
@AjeetGanga Vous avez mentionné comme 'le petit- * (jeu de mots intentionnel), clause de l'auto_ptr, qui n'est pas lu par tous les programmeurs, autorise, attribution d'un auto_ptr à un autre, et transfère la propriété'. Disons que j'ai deux ptr automatiques en tant que a et b en entier. Je fais l'affectation car *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?
VINOTH ENERGETIC
@VINOTHENERGETIC Ajeet parlait d'assigner à un auto_ptrobjet 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?
underscore_d
23

shared_ptrpeuvent être stockés dans des conteneurs. auto_ptrne peut pas.

BTW unique_ptrest vraiment le auto_ptrremplacement direct , il combine les meilleures fonctionnalités des deux std::auto_ptret boost::scoped_ptr.

Ben Voigt
la source
11

Encore une autre façon d'expliquer la différence ...

Fonctionnellement, C ++ 11 std::unique_ptrest 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, ap3discrètement "vole" la propriété de *ap, en laissant le apré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/ structa un std::auto_ptrmembre, alors faire une copie d'une instance sera releasele 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.

Tony Delroy
la source
auto_ptr "vole" tranquillement la propriété +1
camino
3

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
edW
la source