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

C ++ est tout au sujet de la propriété de la mémoire
Aka ” Sémantique de propriété

Il incombe au propriétaire d’un bloc de mémoire alloué dynamicment de libérer cette mémoire. Donc, la question devient vraiment qui possède la mémoire.

En C ++ la propriété est documentée par le type un pointeur RAW est encapsulé dans un bon programme C ++ (OMI) il est très rare de voir des pointeurs RAW contournés (RARE pas NEVER). dire à qui appartient la mémoire et donc sans lire attentivement la documentation, vous ne pouvez pas dire qui est responsable de la propriété).

Inversement, il est rare de voir les pointeurs RAW stockés dans une classe. Chaque pointeur RAW est stocké dans son propre wrapper de pointeur SMART. ( NB: Si vous ne possédez pas d’object, vous ne devriez pas le stocker car vous ne pouvez pas savoir quand il sera hors de scope et détruit.)

Donc, la question:

  • Quel type de propriété sémantique les gens rencontrent-ils?
  • Quelles classes standard sont utilisées pour implémenter ces sémantiques?
  • 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 individuellement

Résumé:

Conceptuellement, les pointeurs intelligents sont simples et les implémentations naïves sont faciles. J’ai vu beaucoup de tentatives d’implémentation, 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 «pointeurs intelligents» bien testés dans une bibliothèque plutôt que de les imprimer vous-même. std :: auto_ptr ou l’un des pointeurs intelligents de boost semblent couvrir tous mes besoins.

std :: auto_ptr :

Personne seule possède l’object.
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

Personne seule possède l’object.
Le transfert de propriété n’est PAS autorisé.

Usage:
======
Utilisé pour montrer la propriété explicite.
L’object sera détruit par le destructeur ou lorsqu’il sera explicitement réinitialisé.

boost :: shared_ptr (std :: tr1 :: shared_ptr )

Propriété multiple
C’est un simple pointeur de référence. Lorsque le compte de référence atteint zéro, l’object est détruit.

Usage:
======
Lorsque l’object 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

Utilisé avec shared_ptr .
Dans des situations où un cycle de pointeurs peut se produire.

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

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

shared_ptr – comptage de référence, désallocation lorsque le compteur atteint zéro

weak_ptr – comme ci-dessus, mais c’est un “esclave” pour un shared_ptr , ne peut pas 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’object doit être considéré comme un seul propriétaire uniquement. Lorsque vous affectez un pointeur à un autre, le second “vole” l’object du premier.

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

Je passe toujours des objects par référence ( const chaque fois que possible), dans ce cas la méthode appelée doit supposer que l’object n’est vivant que pendant la durée de l’appel.

Il y a un autre type de pointeur que j’utilise que j’appelle hub_ptr . C’est lorsque vous avez un object qui doit être accessible à partir d’objects nesteds (généralement en tant que classe de base virtuelle). Cela pourrait être résolu en leur passant un weak_ptr , mais il ne possède pas de shared_ptr . Comme il sait que ces objects ne vivraient pas plus longtemps que lui, il leur passe un hub_ptr (c’est juste un wrapper de template pour un pointeur normal).

Modèle C ++ simple

Dans la plupart des modules que j’ai vus, par défaut, il était supposé que la réception des pointeurs n’était pas la propriété. En fait, les fonctions / méthodes qui abandonnaient la propriété d’un pointeur étaient toutes deux 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 / elle alloue explicitement . Tout le rest est automatiquement éliminé (à la sortie du périmètre ou via RAII). Ceci est un modèle de type C, étendu par le fait que la plupart des pointeurs appartiennent à des objects qui les désalloueront automatiquement ou en cas de besoin (lors de la destruction desdits objects) et que la durée de vie des objects 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 suffisamment intelligent, il utilisera des références à chaque fois que cela est possible).

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

Modèle C ++ pointé intelligent

Dans un code plein de pointeurs intelligents, l’utilisateur peut espérer ignorer la durée de vie des objects. 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 avec 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. Donc, vous avez encore à prendre en considération (le pointeur faible pourrait bien ne rien indiquer, même si son avantage par rapport au pointeur brut est qu’il peut vous le dire).

  • boost :: shared_ptr
  • boost :: faiblesse_ptr

Conclusion

Quels que soient les modèles que je décris, sauf exception, la réception d’un pointeur ne lui appartient pas et il est toujours très important de savoir qui possède qui . Même pour le code C ++ utilisant fortement des références et / ou des pointeurs intelligents.

Ne pas avoir de propriété partagée. Si vous le faites, assurez-vous que c’est seulement avec le code que vous ne contrôlez pas.

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

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

Lorsqu’une ressource est partagée entre plusieurs objects. 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 terminé.

std::tr1::shared_ptr est souvent votre meilleur pari.

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

Sous Windows, il y a les pointeurs COM (IUnknown, IDispatch et friends) et divers pointeurs intelligents pour les gérer (par exemple, CComPtr de l’ATL et les pointeurs intelligents générés automatiquement par l’instruction “import” dans Visual Studio en fonction de la classe _com_ptr ). ).

  • Un propriétaire
  • boost :: scoped_ptr

Lorsque vous avez besoin d’allouer de la mémoire de manière dynamic mais que vous voulez être sûr qu’il est libéré sur chaque sharepoint 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

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

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

Sur la page Web http://yasper.sourceforge.net/, elle est décrite comme suit:

Pourquoi écrire un autre pointeur C ++? Il existe déjà plusieurs implémentations de pointeurs intelligents de haute qualité pour C ++, notamment le panthéon de pointeur Boost et 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 des autres bibliothèques, Yasper est un pointeur de comptage de référence étroitement ciblé. Cela correspond étroitement aux politiques 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 du Boost ou sans avoir à se familiariser avec les modèles de règles complexes 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 les actions risquées (mais utiles) (telles que l’affectation de pointeurs bruts et la libération manuelle) interdites par les autres implémentations. Attention, n’utilisez ces fonctionnalités que si vous savez ce que vous faites!

Il existe une autre forme fréquemment utilisée de propriétaire de transfert unique, et elle est préférable à auto_ptr car elle évite les problèmes provoqués par la corruption insensée de la sémantique d’affectation.

Je ne parle que de l’ swap . Tout type avec une fonction d’ swap appropriée peut être conçu comme une 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, en les échangeant. Chaque instance conserve son identité mais est liée au nouveau contenu. C’est comme une référence à relier en toute sécurité.

(C’est une référence intelligente plutôt qu’un pointeur intelligent, car vous n’avez pas besoin de la déréférencer explicitement pour obtenir le contenu.)

Cela signifie que auto_ptr devient moins nécessaire – il n’est nécessaire que pour combler les lacunes lorsque les types n’ont pas une bonne fonction de swap . Mais tous les conteneurs std font.

  • One Owner: Aka supprimer sur Copy
  • std :: auto_ptr

Lorsque le créateur de l’object souhaite explicitement remettre la propriété à quelqu’un d’autre. C’est aussi une façon de documenter le code que je vous donne et je ne le surveille plus alors assurez-vous de le supprimer lorsque vous avez terminé.