Ok, donc la dernière fois que j'ai écrit C ++ pour gagner ma vie, std::auto_ptr
c'était toute la lib std disponible, et boost::shared_ptr
c'était à la mode. Je n'ai jamais vraiment examiné les autres types de pointeurs intelligents fournis. Je comprends que C ++ 11 fournit maintenant certains des types de boost proposés, mais pas tous.
Quelqu'un a-t-il donc un algorithme simple pour déterminer quand utiliser quel pointeur intelligent? Incluant de préférence des conseils concernant les pointeurs stupides (pointeurs bruts comme T*
) et le reste des pointeurs intelligents boost. (Quelque chose comme ça serait génial).
Réponses:
Propriété partagée:
Le
shared_ptr
etweak_ptr
la norme adoptée sont à peu près les mêmes que leurs homologues Boost . Utilisez-les lorsque vous avez besoin de partager une ressource et que vous ne savez pas laquelle sera la dernière en vie. Utilisezweak_ptr
pour observer la ressource partagée sans influencer sa durée de vie, pas pour rompre les cycles. Les cycles avecshared_ptr
ne devraient normalement pas se produire - deux ressources ne peuvent pas se posséder.Notez que Boost propose en outre
shared_array
, ce qui pourrait être une alternative appropriée àshared_ptr<std::vector<T> const>
.Ensuite, les offres Boost
intrusive_ptr
, qui sont une solution légère si votre ressource propose déjà une gestion comptée par référence et que vous souhaitez l'adapter au principe RAII. Celui-ci n'a pas été adopté par la norme.Propriété unique:
Boost possède également un
scoped_ptr
, qui n'est pas copiable et pour lequel vous ne pouvez pas spécifier de suppresseur.std::unique_ptr
estboost::scoped_ptr
sous stéroïdes et devrait être votre choix par défaut lorsque vous avez besoin d'un pointeur intelligent . Il vous permet de spécifier un suppresseur dans ses arguments de modèle et est mobile , contrairement àboost::scoped_ptr
. Il est également entièrement utilisable dans les conteneurs STL tant que vous n'utilisez pas d'opérations qui nécessitent des types copiables (évidemment).Notez à nouveau, que Boost a une version de tableau:,
scoped_array
que la norme a unifié en exigeantstd::unique_ptr<T[]>
une spécialisation partielle qui feradelete[]
pointer le pointeur au lieu dedelete
le faire (avec ledefault_delete
r).std::unique_ptr<T[]>
propose égalementoperator[]
au lieu deoperator*
etoperator->
.Notez que
std::auto_ptr
c'est toujours dans la norme, mais c'est obsolète .§D.10 [depr.auto.ptr]
Pas de propriété:
utilisez des pointeurs muets (pointeurs bruts) ou des références pour les références non propriétaires aux ressources et lorsque vous savez que la ressource survivra à l'objet / portée de référence. Préférez les références et utilisez des pointeurs bruts lorsque vous avez besoin de nullité ou de réinitialisation.
Si vous voulez une référence non propriétaire à une ressource, mais vous ne savez pas si la ressource survivra à l'objet qui la référence, emballez la ressource dans un
shared_ptr
et utilisez unweak_ptr
- vous pouvez tester si le parentshared_ptr
est vivantlock
, ce qui retourne unshared_ptr
qui est non nul si la ressource existe toujours. Si vous souhaitez tester si la ressource est morte, utilisezexpired
. Les deux peuvent sembler similaires, mais sont très différents face à une exécution simultanée, car ilsexpired
garantissent uniquement sa valeur de retour pour cette seule instruction. Un test apparemment innocent commeest une condition de concurrence potentielle.
la source
shared_ptr
et le pointeur non propriétaire pour être aweak_ptr
...shared_array<T>
est une alternative àshared_ptr<T[]>
ne pas faireshared_ptr<vector<T>>
: il ne peut pas grandir.Décider quel pointeur intelligent utiliser est une question de propriété . En ce qui concerne la gestion des ressources, l'objet A possède l' objet B s'il contrôle la durée de vie de l'objet B. Par exemple, les variables membres appartiennent à leurs objets respectifs car la durée de vie des variables membres est liée à la durée de vie de l'objet. Vous choisissez des pointeurs intelligents en fonction de la propriété de l'objet.
Notez que la propriété d'un système logiciel est distincte de la propriété comme nous le penserions en dehors du logiciel. Par exemple, une personne peut «posséder» sa maison, mais cela ne signifie pas nécessairement qu'un
Person
objet a le contrôle sur la durée de vie d'unHouse
objet. La confrontation de ces concepts du monde réel avec des concepts logiciels est une façon infaillible de vous programmer dans un trou.Si vous êtes l'unique propriétaire de l'objet, utilisez
std::unique_ptr<T>
.Si vous avez partagé la propriété de l'objet ...
- S'il n'y a pas de cycle de propriété, utilisez
std::shared_ptr<T>
.- S'il y a des cycles, définir une "direction" et utiliser
std::shared_ptr<T>
dans un sens etstd::weak_ptr<T>
dans l'autre.Si l'objet vous appartient, mais qu'il est possible de ne pas avoir de propriétaire, utilisez des pointeurs normaux
T*
(par exemple, des pointeurs parents).Si l'objet vous appartient (ou a une existence garantie d'une autre manière), utilisez des références
T&
.Avertissement: soyez conscient des coûts des pointeurs intelligents. Dans les environnements à mémoire ou performances limitées, il peut être avantageux d'utiliser simplement des pointeurs normaux avec un schéma plus manuel pour gérer la mémoire.
Les coûts:
std::shared_ptr
a la surcharge d'un incrément de comptage de référence lors de la copie, plus un décrément lors de la destruction suivi d'une vérification de 0 comptage avec suppression de l'objet retenu. Selon l'implémentation, cela peut alourdir votre code et entraîner des problèmes de performances.Exemples:
Un arbre binaire ne possède pas son parent, mais l'existence d'un arbre implique l'existence de son parent (ou
nullptr
pour root), de sorte qu'il utilise un pointeur normal. Un arbre binaire (avec une sémantique de valeur) a la propriété exclusive de ses enfants, donc ce sontstd::unique_ptr
.Ici, le nœud de liste possède ses listes suivante et précédente, nous définissons donc une direction et utilisons
shared_ptr
pour suivant etweak_ptr
pour prev pour rompre le cycle.la source
shared_ptr<BinaryTree>
pour les enfants etweak_ptr<BinaryTree>
pour la relation parentale.Utilisez
unique_ptr<T>
tout le temps sauf lorsque vous avez besoin d'un comptage de référence, auquel cas utilisezshared_ptr<T>
(et dans de très rares cas,weak_ptr<T>
pour éviter les cycles de référence). Dans presque tous les cas, la propriété unique transférable est très bien.Pointeurs bruts: bon uniquement si vous avez besoin de retours covariants, pointage non propriétaire qui peut se produire. Ils ne sont pas terriblement utiles autrement.
Pointeurs de tableau:
unique_ptr
a une spécialisation pourT[]
laquelle appelle automatiquementdelete[]
le résultat, vous pouvez donc le faire en toute sécuritéunique_ptr<int[]> p(new int[42]);
par exemple.shared_ptr
vous auriez toujours besoin d'un suppresseur personnalisé, mais vous n'auriez pas besoin d'un pointeur de tableau partagé ou unique spécialisé. Bien sûr, de telles choses sont généralement mieux remplacées par destd::vector
toute façon. Malheureusement,shared_ptr
ne fournit pas de fonction d'accès au tableau, vous devrez donc toujours appeler manuellementget()
, maisunique_ptr<T[]>
fournit à laoperator[]
place deoperator*
etoperator->
. Dans tous les cas, vous devez vous contrôler vous-même. Cela rendshared_ptr
légèrement moins convivial, bien que sans doute l'avantage générique et qu'aucune dépendance Boost ne fasseunique_ptr
etshared_ptr
les gagnants à nouveau.Pointeurs étendus: rendus non pertinents par
unique_ptr
, tout commeauto_ptr
.Il n'y a vraiment plus rien. En C ++ 03 sans sémantique de déplacement, cette situation était très compliquée, mais en C ++ 11, le conseil est très simple.
Il existe encore des utilisations pour d'autres pointeurs intelligents, comme
intrusive_ptr
ouinterprocess_ptr
. Cependant, ils sont très niches et complètement inutiles dans le cas général.la source
std::unique_ptr<T[]>
fournitoperator[]
au lieu deoperator*
etoperator->
. Il est vrai que vous devez toujours vérifier vous-même les limites.Cas d'utilisation
unique_ptr
:Cas d'utilisation
shared_ptr
:Cas d'utilisation
weak_ptr
:N'hésitez pas à modifier et ajouter plus
la source