228 votes

Les pointeurs intelligents (boost) expliqués

Quelle est la différence entre les séries de pointeurs suivantes ? Quand utilisez-vous chaque pointeur dans un code de production, si c'est le cas ?

Des exemples seraient appréciés !

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

Edit#1

Utilisez-vous Boost dans votre code de production ?

349voto

Johannes Schaub - litb Points 256113

Propriétés de base des pointeurs intelligents

C'est facile lorsque vous avez des propriétés que vous pouvez attribuer à chaque pointeur intelligent. Il existe trois propriétés importantes.

  • pas de propriété du tout
  • transfert de propriété
  • part de propriété

La première signifie qu'un pointeur intelligent ne peut pas supprimer l'objet, car il ne le possède pas. La seconde signifie qu'un seul pointeur intelligent peut pointer vers le même objet au même moment. Si le pointeur intelligent doit être renvoyé par des fonctions, la propriété est transférée au pointeur intelligent renvoyé, par exemple.

La troisième signifie que plusieurs pointeurs intelligents peuvent pointer vers le même objet en même temps. Cela s'applique à un pointeur brut également, mais les pointeurs bruts manquent d'une caractéristique importante : Ils ne définissent pas s'ils sont posséder ou non. Un pointeur intelligent de partage de propriété supprimera l'objet si chaque propriétaire renonce à l'objet. Ce comportement est souvent nécessaire, c'est pourquoi les pointeurs intelligents à propriété partagée sont largement répandus.

Certains pointeurs intelligents propriétaires ne supportent ni le deuxième ni le troisième. Ils ne peuvent donc pas être retournés par des fonctions ou transmis ailleurs. Ce qui est le plus approprié pour RAII où le pointeur intelligent est conservé localement et est juste créé pour libérer un objet après qu'il soit sorti de sa portée.

Le partage de la propriété peut être implémenté en ayant un constructeur de copie. Celui-ci copie naturellement un pointeur intelligent et la copie et l'original feront référence au même objet. Le transfert de propriété ne peut pas vraiment être implémenté en C++ actuellement, car il n'y a aucun moyen de transférer quelque chose d'un objet à un autre supporté par le langage : Si vous essayez de retourner un objet depuis une fonction, ce qui se passe est que l'objet est copié. Ainsi, un pointeur intelligent qui implémente le transfert de propriété doit utiliser le constructeur de copie pour implémenter ce transfert de propriété. Cependant, cela empêche son utilisation dans les conteneurs, car les exigences stipulent un certain comportement du constructeur de copie des éléments des conteneurs qui est incompatible avec ce comportement dit de "constructeur mobile" de ces pointeurs intelligents.

C++1x fournit un support natif pour le transfert de propriété en introduisant ce qu'on appelle les "constructeurs de déplacement" et les "opérateurs d'affectation de déplacement". Il est également fourni avec un pointeur intelligent de transfert de propriété appelé unique_ptr .

Catégorisation des pointeurs intelligents

scoped_ptr est un pointeur intelligent qui n'est ni transférable ni partageable. Il est juste utilisable si vous avez besoin d'allouer localement de la mémoire, mais assurez-vous qu'il soit libéré à nouveau lorsqu'il sort de sa portée. Mais il peut toujours être échangé avec un autre scoped_ptr, si vous souhaitez le faire.

shared_ptr est un pointeur intelligent qui partage la propriété (troisième type ci-dessus). Il est compté par référence, ce qui lui permet de savoir quand sa dernière copie sort du champ d'application et de libérer l'objet géré.

weak_ptr est un pointeur intelligent non propriétaire. Il est utilisé pour référencer un objet géré (géré par un shared_ptr) sans ajouter un compte de référence. Normalement, vous devriez extraire le pointeur brut du shared_ptr et le copier. Mais cela ne serait pas sûr, car vous n'auriez aucun moyen de vérifier quand l'objet a été réellement supprimé. Donc, weak_ptr fournit des moyens en référençant un objet géré par shared_ptr. Si vous avez besoin d'accéder à l'objet, vous pouvez verrouiller la gestion de celui-ci (pour éviter que dans un autre thread un shared_ptr le libère pendant que vous utilisez l'objet) et ensuite l'utiliser. Si le weak_ptr pointe vers un objet déjà supprimé, il vous le fera remarquer en lançant une exception. L'utilisation du weak_ptr est plus avantageuse lorsque vous avez une référence cyclique : Le comptage de références ne peut pas facilement faire face à une telle situation.

intrusive_ptr est comme un shared_ptr mais il ne garde pas le compte de référence dans un shared_ptr mais laisse l'incrémentation/décrémentation du compte à quelques fonctions d'aide qui doivent être définies par l'objet qui est géré. Ceci a l'avantage qu'un objet déjà référencé (qui a un compte de référence incrémenté par un mécanisme de comptage de référence externe) peut être placé dans un intrusive_ptr - parce que le compte de référence n'est plus interne au pointeur intelligent, mais le pointeur intelligent utilise un mécanisme de comptage de référence existant.

unique_ptr est un pointeur de transfert de propriété. Vous ne pouvez pas le copier, mais vous pouvez le déplacer en utilisant les constructeurs de déplacement de C++1x :

unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!

C'est la sémantique à laquelle std::auto_ptr obéit, mais en raison de l'absence de support natif pour le déplacement, elle ne peut les fournir sans pièges. unique_ptr volera automatiquement des ressources à un autre unique_ptr temporaire, ce qui est l'une des caractéristiques clés de la sémantique de déplacement. auto_ptr sera déprécié dans la prochaine version du standard C++ en faveur de unique_ptr. C++1x permettra également de placer des objets qui sont seulement déplaçables mais pas copiables dans des conteneurs. Vous pouvez donc insérer des unique_ptr dans un vecteur par exemple. Je m'arrête ici et vous renvoie à un bel article à ce sujet si vous voulez en savoir plus.

92voto

Doug T. Points 33360

scoped_ptr est le plus simple. Lorsqu'il sort de sa portée, il est détruit. Le code suivant est illégal (les scoped_ptrs ne sont pas copiables) mais permet d'illustrer un point :

std::vector< scoped_ptr<T> > tPtrVec;
{
     scoped_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // raw T* is freed
}
tPtrVec[0]->DoSomething(); // accessing freed memory

partagé_ptr est compté comme référence. Chaque fois qu'une copie ou une affectation se produit, le compte de références est incrémenté. Chaque fois que le destructeur d'une instance est activé, le nombre de références pour le T* brut est décrémenté. Lorsqu'il est égal à 0, le pointeur est libéré.

std::vector< shared_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     // This copy to tPtrVec.push_back and ultimately to the vector storage
     // causes the reference count to go from 1->2
     tPtrVec.push_back(tPtr);
     // num references to T goes from 2->1 on the destruction of tPtr
}
tPtrVec[0]->DoSomething(); // raw T* still exists, so this is safe

weak_ptr est une référence faible à un pointeur partagé qui nécessite de vérifier si le pointeur pointé est toujours présent.

std::vector< weak_ptr<T> > tPtrVec;
{
     shared_ptr<T> tPtr(new T());
     tPtrVec.push_back(tPtr);
     // num references to T goes from 1->0
}
shared_ptr<T> tPtrAccessed =  tPtrVec[0].lock();
if (tPtrAccessed[0].get() == 0)
{
     cout << "Raw T* was freed, can't access it"
}
else
{
     tPtrVec[0]->DoSomething(); // raw 
}

intrusive_ptr est typiquement utilisé lorsqu'il y a un ptr intelligent tiers que vous devez utiliser. Elle appelle une fonction libre pour ajouter et décrémenter le nombre de références. lien à la documentation de boost pour plus d'informations.

20voto

timday Points 14860

Ne négligez pas boost::ptr_container dans toute enquête sur les pointeurs intelligents de boost. Ils peuvent s'avérer précieux dans les situations où un p. ex. std::vector<boost::shared_ptr<T> > serait trop lent.

12voto

Anonymous Points 11017

Je suis d'accord avec le conseil de regarder la documentation. Ce n'est pas aussi effrayant qu'il n'y paraît. Et quelques petits conseils :

  • scoped_ptr - un pointeur automatiquement supprimé lorsqu'il sort de sa portée. Note - pas d'affectation possible, mais n'introduit pas de frais généraux.
  • intrusive_ptr - pointeur de comptage de référence sans surcharge de smart_ptr . Cependant, l'objet lui-même stocke le nombre de références
  • weak_ptr - travaille en collaboration avec shared_ptr pour gérer les situations résultant de dépendances circulaires (lisez la documentation, et cherchez sur google pour une belle image ;)
  • shared_ptr - le pointeur générique, le plus puissant (et le plus lourd) des pointeurs intelligents (parmi ceux proposés par boost)
  • Il y a aussi de vieux auto_ptr qui garantit que l'objet vers lequel il pointe est détruit automatiquement lorsque le contrôle quitte une portée. Cependant, il a une sémantique de copie différente du reste des types.
  • unique_ptr - viendra avec C++0x

Réponse à l'édition : Oui

8voto

Pesto Points 16648

Pourquoi ne pas essayer le documentation sur le boost ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X