web-dev-qa-db-fra.com

Smart Pointers: Ou à qui appartient votre bébé?

C++ est une question de propriété de mémoire
Aka "Sémantique de la propriété"

Il est de la responsabilité du propriétaire d'un morceau de mémoire allouée dynamiquement de libérer cette mémoire. Donc, la question devient vraiment à qui appartient la mémoire.

En C++, la propriété est documentée par le type dans lequel un pointeur RAW est enveloppé, donc dans un bon programme C++ (IMO), il est très rare [RARE pas JAMAIS] de voir les pointeurs RAW circuler (comme les pointeurs RAW n'ont pas de propriété inférée, nous ne pouvons donc pas dire à qui appartient la mémoire et donc sans lecture attentive de la documentation, vous ne pouvez pas dire qui est responsable de la propriété).

Inversement, il est rare de voir des pointeurs RAW stockés dans une classe chaque pointeur RAW est stocké dans son propre wrapper de pointeur SMART. (N.B .: Si vous ne possédez pas d'objet, vous ne devriez pas le stocker car vous ne pouvez pas savoir quand il sortira du champ d'application et sera détruit.)

Donc la question:

  • Quel type de propriété sémantique les gens ont-ils rencontré?
  • Quelles classes standard sont utilisées pour implémenter cette sémantique?
  • Quelles situations les trouvez-vous utiles?

Permet de conserver 1 type de propriété sémantique par réponse afin qu'ils puissent être votés de haut en bas individuellement

Sommaire:

Conceptuellement, les pointeurs intelligents sont simples et les implémentations naïves sont faciles. J'ai vu de nombreuses tentatives de mise en œuvre, mais elles sont invariablement brisées d'une manière qui n'est pas évidente pour une utilisation occasionnelle et des exemples. Je recommande donc de toujours utiliser des "Smart Pointers" bien testés à partir d'une bibliothèque plutôt que de rouler le vôtre. std :: auto_ptr ou l'un des pointeurs intelligents boost semble couvrir tous mes besoins.

std :: auto_ptr <T>:

Une personne seule est propriétaire de l'objet.
Mais le transfert de propriété est autorisé.

Usage:
======
Cela vous permet de définir des interfaces qui montrent le transfert explicite de propriété.

boost :: scoped_ptr <T>

Une personne seule est propriétaire de l'objet.
Le transfert de propriété n'est PAS autorisé.

Usage:
======
Utilisé pour montrer la propriété explicite.
L'objet sera détruit par le destructeur ou lors d'une réinitialisation explicite.

boost :: shared_ptr <T> (std :: tr1 :: shared_ptr <T>)

Propriété multiple.
Il s'agit d'un simple pointeur compté par référence. Lorsque le nombre de références atteint zéro, l'objet est détruit.

Usage:
======
Lorsque l'objet peut avoir plusieurs eurs avec une durée de vie qui ne peut pas être déterminée au moment de la compilation.

boost :: faiblesse_ptr <T>

Utilisé avec shared_ptr <T>.
Dans les situations où un cycle de pointeurs peut se produire.

Usage:
======
Utilisé pour empêcher les cycles de conserver des objets lorsque seul le cycle maintient un recomptage partagé.

113
Martin York

Pour moi, ces 3 types couvrent la plupart de mes besoins:

shared_ptr - comptage des références, désallocation lorsque le compteur atteint zéro

weak_ptr - comme ci-dessus, mais c'est un "esclave" pour un shared_ptr, impossible de désallouer

auto_ptr - lorsque la création et la désallocation se produisent à l'intérieur de la même fonction, ou lorsque l'objet doit être considéré comme un seul propriétaire. Lorsque vous affectez un pointeur à un autre, le second "vole" l'objet au premier.

J'ai ma propre implémentation pour ceux-ci, mais ils sont également disponibles dans Boost.

Je passe toujours des objets par référence (const chaque fois que possible), dans ce cas, la méthode appelée doit supposer que l'objet n'est vivant que pendant le temps de l'appel.

Il y a un autre type de pointeur que j'utilise que j'appelle hub_ptr . C'est quand vous avez un objet qui doit être accessible à partir d'objets imbriqués en lui (généralement en tant que classe de base virtuelle). Cela pourrait être résolu en passant un weak_ptr pour eux, mais il n'a pas de shared_ptr à lui-même. Comme il sait que ces objets ne vivraient pas plus longtemps que lui, il leur transmet un hub_ptr (c'est juste un wrapper de modèle pour un pointeur normal).

20
Fabio Ceconello

Modèle C++ simple

Dans la plupart des modules que j'ai vus, par défaut, on supposait que recevoir des pointeurs était pas recevoir la propriété. En fait, les fonctions/méthodes abandonnant la propriété d'un pointeur étaient à la fois très rares et exprimaient explicitement ce fait dans leur documentation.

Ce modèle suppose que l'utilisateur n'est propriétaire que de ce qu'il alloue explicitement. Tout le reste est automatiquement éliminé (à la sortie de la portée ou via RAII). Il s'agit d'un modèle de type C, étendu par le fait que la plupart des pointeurs appartiennent à des objets qui les désalloueront automatiquement ou en cas de besoin (lors de la destruction desdits objets, principalement), et que la durée de vie des objets est prévisible (RAII est votre ami, encore).

Dans ce modèle, les pointeurs bruts circulent librement et ne sont généralement pas dangereux (mais si le développeur est assez intelligent, il utilisera des références à la place chaque fois que possible).

  • pointeurs bruts
  • std :: auto_ptr
  • boost :: scoped_ptr

Modèle C++ pointu intelligent

Dans un code plein de pointeurs intelligents, l'utilisateur peut espérer ignorer la durée de vie des objets. Le propriétaire n'est jamais le code utilisateur: c'est le pointeur intelligent lui-même (RAII, encore). Le problème est que les références circulaires mélangées à des pointeurs intelligents comptés par référence peuvent être mortelles, vous devez donc gérer à la fois les pointeurs partagés et les pointeurs faibles. Vous avez donc encore la propriété à considérer (le pointeur faible pourrait bien pointer vers rien, même si son avantage sur le pointeur brut est qu'il peut vous le dire).

  • boost :: shared_ptr
  • boost :: faiblesse_ptr

Conclusion

Peu importe les modèles que je décris, sauf exception, recevoir un pointeur n'est pas pas recevoir sa propriété et c'est encore très important de savoir à qui appartient qui. Même pour le code C++ utilisant fortement des références et/ou des pointeurs intelligents.

23
paercebal

N'ayez pas de propriété partagée. Si vous le faites, assurez-vous que ce n'est qu'avec du code que vous ne contrôlez pas.

Cela résout 100% des problèmes, car cela vous oblige à comprendre comment tout interagit.

10
MSN
  • Propriété partagée
  • boost :: shared_ptr

Lorsqu'une ressource est partagée entre plusieurs objets. Le boost shared_ptr utilise le comptage de références pour s'assurer que la ressource est désallouée lorsque tout le monde est fini.

2
Martin York

std::tr1::shared_ptr<Blah> est souvent votre meilleur choix.

2
Matt Cruikshank

De boost, il y a aussi la bibliothèque conteneur de pointeurs . Ceux-ci sont un peu plus efficaces et plus faciles à utiliser qu'un conteneur standard de pointeurs intelligents, si vous n'utilisez les objets que dans le contexte de leur conteneur.

Sous Windows, il existe des pointeurs COM (IUnknown, IDispatch et amis) et divers pointeurs intelligents pour les gérer (par exemple, les ATL CComPtr et les pointeurs intelligents générés automatiquement par l'instruction "import" dans Visual Studio basé sur la classe _ com_ptr ).

2
Ryan Ginstrom
  • Un propriétaire
  • boost :: scoped_ptr

Lorsque vous devez allouer de la mémoire de manière dynamique mais que vous voulez vous assurer qu'elle est désallouée à chaque point de sortie du bloc.

Je trouve cela utile car il peut facilement être réinstallé et libéré sans jamais avoir à se soucier d'une fuite

1
Pieter

Je ne pense pas avoir jamais été en mesure de partager la propriété de ma conception. En fait, du haut de ma tête, le seul cas valable auquel je puisse penser est le modèle Flyweight.

1
Nemanja Trifunovic

yasper :: ptr est une alternative légère, boost :: shared_ptr. Cela fonctionne bien dans mon (pour l'instant) petit projet.

Dans la page Web à http://yasper.sourceforge.net/ il est décrit comme suit:

Pourquoi écrire un autre pointeur intelligent C++? Il existe déjà plusieurs implémentations de pointeur intelligent de haute qualité pour C++, en particulier le panthéon de pointeur Boost et le SmartPtr de Loki. Pour une bonne comparaison des implémentations de pointeurs intelligents et lorsque leur utilisation est appropriée, veuillez lire The New C++: Smart (er) Pointers de Herb Sutter. Contrairement aux fonctionnalités étendues d'autres bibliothèques, Yasper est un pointeur de comptage de référence étroitement ciblé. Il correspond étroitement aux règles shared_ptr de Boost et RefCounted/AllowConversion de Loki. Yasper permet aux programmeurs C++ d'oublier la gestion de la mémoire sans introduire les grandes dépendances de Boost ni avoir à se renseigner sur les modèles de politique compliqués de Loki. Philosophie

* small (contained in single header)
* simple (nothing fancy in the code, easy to understand)
* maximum compatibility (drop in replacement for dumb pointers)

Le dernier point peut être dangereux, car yasper autorise des actions risquées (mais utiles) (telles que l'affectation à des pointeurs bruts et la libération manuelle) interdites par d'autres implémentations. Attention, n'utilisez ces fonctionnalités que si vous savez ce que vous faites!

1
Hernán

Il existe une autre forme fréquemment utilisée de propriétaire unique transférable, et il est préférable de auto_ptr car cela évite les problèmes causés par auto_ptr la folle corruption de la sémantique des affectations.

Je ne parle de rien d'autre que swap. Tout type avec une fonction swap appropriée peut être conçu comme un référence intelligente à un contenu qu'il possède jusqu'à ce que la propriété soit transférée à une autre instance du même type, par les échanger. Chaque instance conserve son identité mais est liée à un nouveau contenu. C'est comme une référence qui peut être reliée en toute sécurité.

(Il s'agit d'une référence intelligente plutôt que d'un pointeur intelligent, car vous n'avez pas à le déréférencer explicitement pour accéder au contenu.)

Cela signifie que auto_ptr devient moins nécessaire - il est seulement nécessaire de combler les lacunes où les types n'ont pas une bonne fonction swap. Mais tous les conteneurs standard le font.

1
Daniel Earwicker
  • Un propriétaire: Aka supprimer lors de la copie
  • std :: auto_ptr

Lorsque le créateur de l'objet veut confier explicitement la propriété à quelqu'un d'autre. C'est aussi un moyen de documenter dans le code que je vous donne cela et je ne le surveille plus, alors assurez-vous de le supprimer lorsque vous avez terminé.

0
Martin York