web-dev-qa-db-fra.com

pointeurs intelligents (boost) a expliqué

Quelle est la différence entre les pointeurs suivants? Quand utilisez-vous chaque pointeur dans le code de production, voire pas du tout?

Des exemples seraient appréciés!

  1. scoped_ptr

  2. shared_ptr

  3. weak_ptr

  4. intrusive_ptr

Utilisez-vous boost dans le code de production?

214
Sasha

Propriétés de base des pointeurs intelligents

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

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

Le premier 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 jamais pointer sur le même objet en même temps. Si le pointeur intelligent doit être renvoyé à partir de fonctions, la propriété est transférée au pointeur intelligent renvoyé, par exemple.

Le troisième signifie que plusieurs pointeurs intelligents peuvent pointer sur le même objet en même temps. Ceci s'applique également à un pointeur brut, mais les pointeurs bruts n'ont pas de caractéristique importante: ils ne définissent pas s'ils sont propriétaires ou non. Un pointeur intelligent de partage de propriété supprimera l'objet si chaque propriétaire le lui abandonne. Ce comportement est souvent nécessaire, de sorte que les pointeurs intelligents partagés sont largement répandus.

Certains propriétaires de pointeurs intelligents ne prennent en charge ni le second ni le troisième. Ils ne peuvent donc pas être retournés à partir de fonctions ou passés ailleurs. Ce qui convient le mieux à RAII fins où le pointeur intelligent est conservé localement et vient d'être créé pour libérer un objet après sa sortie du champ d'application.

La part de propriété peut être implémentée en ayant un constructeur de copie. Cela copie naturellement un pointeur intelligent et la copie et l'original référenceront le 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 supporté par le langage: si vous essayez de retourner un objet à partir d'une fonction, ce qui se passe, c'est que l'objet est copié. Ainsi, un pointeur intelligent implémentant le transfert de propriété doit utiliser le constructeur de copie pour implémenter ce transfert de propriété. Cependant, cela rompt à son tour son utilisation dans les conteneurs, car les exigences indiquent un certain comportement du constructeur de la copie d'éléments de conteneurs qui est incompatible avec ce comportement dit "constructeur en mouvement" de ces pointeurs intelligents.

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

Catégoriser les pointeurs intelligents

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

shared_ptr est un pointeur intelligent qui partage la propriété (troisième type ci-dessus). Il s'agit d'un décompte de références afin qu'il puisse voir quand la dernière copie de celui-ci sort du champ d'application et libère ensuite 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 de compte de référence. Normalement, vous devez extraire le pointeur brut de shared_ptr et le copier. Mais cela ne serait pas sûr, car vous n'auriez pas le moyen de vérifier quand l'objet a été réellement supprimé. Ainsi, faible_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 en verrouiller la gestion (pour éviter que dans un autre thread, un shared_ptr le libère pendant que vous utilisez l'objet), puis l'utiliser. Si faible_ptr pointe sur un objet déjà supprimé, il vous remarquera en lançant une exception. Il est très utile d’utiliser faibles_ptr 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 conserve pas le compte de référence dans un shared_ptr mais laisse incrémenter/décrémenter le compte pour certaines fonctions d’aide devant être définies par l’objet géré. Cela présente l’avantage qu’un objet déjà référencé (qui a un compte de références incrémenté par un mécanisme de comptage de références externe) puisse être inséré dans un intrusive_ptr - car le compte de références n’est plus interne au pointeur intelligent, mais le pointeur intelligent utilise un objet existant. mécanisme de comptage des références.

unique_ptr est un pointeur de transfert de propriété. Vous ne pouvez pas le copier, mais vous pouvez le déplacer à l'aide des 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 à cause du manque de support natif pour le déplacement, il ne parvient pas à les fournir sans pièges. unique_ptr volera automatiquement les ressources d'un autre unique unique_ptr temporaire, qui est l'une des fonctionnalités clés de la sémantique du déplacement. auto_ptr sera obsolète dans la prochaine version de C++ Standard au profit de unique_ptr. C++ 1x autorisera également le bourrage d'objets uniquement déplaçables mais non copiables dans des conteneurs. Vous pouvez donc insérer unique_ptr dans un vecteur, par exemple. Je vais m'arrêter ici et vous faire référence à n bel article à ce sujet si vous souhaitez en savoir plus à ce sujet.

334

scoped_ptr est le plus simple. Quand cela sort du cadre, il est détruit. Le code suivant est illégal (scoped_ptrs n'est pas copiable) mais illustrera 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

shared_ptr est la référence comptée. Chaque fois qu'une copie ou une affectation se produit, le compte de référence est incrémenté. Chaque fois que le destructeur d'une instance est déclenché, le compte de références pour le T * brut est décrémenté. Une fois que c'est 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

faible_ptr est une référence faible à un pointeur partagé qui nécessite de vérifier si le shared_ptr pointé est toujours là.

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 généralement utilisé lorsqu'un ptr intelligent tiers doit être utilisé. Il appellera une fonction libre pour ajouter et décrémenter le nombre de références. Voir le lien pour augmenter la documentation pour plus d’informations.

90
Doug T.

Ne négligez pas boost::ptr_container dans toute enquête sur les pointeurs intelligents boostés. Ils peuvent être inestimables dans les situations où un e.g std::vector<boost::shared_ptr<T> > Serait trop lent.

20
timday

J'appuie les conseils concernant la consultation de la documentation. Ce n'est pas aussi effrayant qu'il y paraît. Et quelques petites astuces:

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

Réponse à modifier: Oui

12
Anonymous