Comparaisons, avantages, inconvénients et quand les utiliser?
Il s'agit d'un spin-off d'un thread de ramassage des ordures où ce que je pensais être une réponse simple a généré de nombreux commentaires sur certaines implémentations de pointeurs intelligents spécifiques, il semblait donc utile de commencer un nouveau message.
En fin de compte, la question est de savoir quelles sont les différentes implémentations de pointeurs intelligents en C ++ et comment se comparent-elles? Juste de simples avantages et inconvénients ou exceptions et des tentatives pour quelque chose que vous pourriez penser autrement devrait fonctionner.
J'ai publié quelques implémentations que j'ai utilisées ou du moins ignorées et que j'ai envisagé d'utiliser comme réponse ci-dessous et ma compréhension de leurs différences et similitudes qui peuvent ne pas être précises à 100%, alors n'hésitez pas à vérifier les faits ou à me corriger si nécessaire.
L'objectif est de découvrir de nouveaux objets et bibliothèques ou de corriger mon utilisation et ma compréhension des implémentations existantes déjà largement utilisées et de me retrouver avec une référence décente pour les autres.
la source
osg::ref_ptr
.std::auto_ptr
.std::auto_ptr_ref
est un détail de conception destd::auto_ptr
.std::auto_ptr
n'a rien à voir avec le garbage collection, son objectif principal est spécifiquement de permettre un transfert de propriété en toute sécurité, en particulier dans les situations d'appel de fonction et de retour de fonction.std::unique_ptr
ne peut résoudre les "problèmes" que vous citez avec les conteneurs standard parce que C ++ a changé pour permettre une distinction entre le déplacement et la copie et les conteneurs standard ont changé pour profiter de cela.auto_ptr_ref
être un détail d'implémentation). Pourtant, je suis d'accord que vous devriez poster ceci comme une réponse et reformuler la question pour être une question réelle. Cela peut ensuite servir de référence future.Réponses:
C ++ 03
std::auto_ptr
- Peut-être l'un des originaux souffrait-il du syndrome du premier projet ne fournissant que des installations de collecte des ordures limitées. Le premier inconvénient est qu'il fait appeldelete
à la destruction, ce qui les rend inacceptables pour contenir des objets alloués au tableau (new[]
). Il prend possession du pointeur de sorte que deux pointeurs automatiques ne doivent pas contenir le même objet. L'affectation transférera la propriété et réinitialisera le pointeur automatique rvalue à un pointeur nul. Ce qui conduit peut-être au pire inconvénient; ils ne peuvent pas être utilisés dans des conteneurs STL en raison de l'impossibilité de copier susmentionnée. Le coup final à tout cas d'utilisation est qu'ils devraient être obsolètes dans le prochain standard de C ++.std::auto_ptr_ref
- Ce n'est pas un pointeur intelligent, c'est en fait un détail de conception utilisé en conjonction avecstd::auto_ptr
pour permettre la copie et l'affectation dans certaines situations. Plus précisément, il peut être utilisé pour convertir un non-conststd::auto_ptr
en une valeur l en utilisant l'astuce Colvin-Gibbons également connue sous le nom de constructeur de mouvement pour transférer la propriété.Au contraire, peut-être
std::auto_ptr
n'était-il pas vraiment destiné à être utilisé comme un pointeur intelligent à usage général pour le ramasse-miettes automatique. La plupart de mes connaissances et hypothèses limitées sont basées sur l'utilisation efficace d'auto_ptr par Herb Sutter et je l'utilise régulièrement, mais pas toujours de la manière la plus optimisée.C ++ 11
std::unique_ptr
- C'est notre ami qui va remplacerstd::auto_ptr
ce sera tout à fait similaire , sauf avec les principales améliorations pour corriger les faiblesses destd::auto_ptr
travailler avec des tableaux, lvalue protection via un constructeur de copie privée, étant utilisable avec des conteneurs STL et des algorithmes, etc. Comme il est au dessus de la performance et l'empreinte mémoire est limitée, c'est un candidat idéal pour remplacer, ou peut-être plus justement décrit comme possédant, des pointeurs bruts. Comme l'indique "unique", il n'y a qu'un seul propriétaire du pointeur comme le précédentstd::auto_ptr
.std::shared_ptr
- Je crois que cela est basé sur TR1 etboost::shared_ptr
mais amélioré pour inclure également l'aliasing et l'arithmétique du pointeur. En bref, il enveloppe un pointeur intelligent compté par référence autour d'un objet alloué dynamiquement. Comme l'indique "partagé", le pointeur peut appartenir à plus d'un pointeur partagé lorsque la dernière référence du dernier pointeur partagé sort de la portée, l'objet sera supprimé de manière appropriée. Ils sont également thread-safe et peuvent gérer des types incomplets dans la plupart des cas.std::make_shared
peut être utilisé pour construire efficacement unestd::shared_ptr
allocation avec une seule pile en utilisant l'allocateur par défaut.std::weak_ptr
- De même basé sur TR1 etboost::weak_ptr
. Il s'agit d'une référence à un objet appartenant à astd::shared_ptr
et n'empêchera donc pas la suppression de l'objet si lestd::shared_ptr
nombre de références tombe à zéro. Pour accéder au pointeur brut, vous devez d'abord accéder austd::shared_ptr
en appelantlock
ce qui renverra un videstd::shared_ptr
si le pointeur possédé a expiré et a déjà été détruit. Ceci est principalement utile pour éviter des décomptes de références suspendus indéfinis lors de l'utilisation de plusieurs pointeurs intelligents.Renforcer
boost::shared_ptr
- Probablement le plus simple à utiliser dans les scénarios les plus variés (STL, PIMPL, RAII, etc.), il s'agit d'un pointeur intelligent compté référencé partagé. J'ai entendu quelques plaintes au sujet des performances et des frais généraux dans certaines situations, mais j'ai dû les ignorer car je ne me souviens pas de ce qu'était l'argument. Apparemment, il était assez populaire pour devenir un objet C ++ standard en attente et aucun inconvénient par rapport à la norme concernant les pointeurs intelligents ne vient à l'esprit.boost::weak_ptr
- Tout comme la description précédente destd::weak_ptr
, basée sur cette implémentation, cela permet une référence non propriétaire à un fichierboost::shared_ptr
. Vous appelez sans surpriselock()
pour accéder au pointeur partagé "fort" et devez vérifier qu'il est valide car il aurait déjà pu être détruit. Assurez-vous simplement de ne pas stocker le pointeur partagé retourné et laissez-le sortir de la portée dès que vous en avez terminé, sinon vous revenez au problème de référence cyclique où vos comptages de références se bloqueront et les objets ne seront pas détruits.boost::scoped_ptr
- Il s'agit d'une classe de pointeur intelligent simple avec peu de frais généraux probablement conçue pour une alternative plus performante queboost::shared_ptr
lorsqu'elle est utilisable. C'est comparablestd::auto_ptr
notamment dans le fait qu'il ne peut pas être utilisé en toute sécurité en tant qu'élément d'un conteneur STL ou avec plusieurs pointeurs vers le même objet.boost::intrusive_ptr
- Je ne l'ai jamais utilisé mais d'après ce que j'ai compris, il est conçu pour être utilisé lors de la création de vos propres classes compatibles avec les pointeurs intelligents. Vous devez implémenter vous-même le comptage de références, vous devrez également implémenter quelques méthodes si vous voulez que votre classe soit générique, en plus vous devrez implémenter votre propre sécurité des threads. Sur le plan positif, cela vous donne probablement la manière la plus personnalisée de choisir et de choisir exactement combien ou combien peu d '«intelligence» vous voulez.intrusive_ptr
est généralement plus efficace queshared_ptr
car il vous permet d'avoir une seule allocation de tas par objet. (merci Arvid)boost::shared_array
- Ceci est unboost::shared_ptr
pour les tableaux. Fondamentalementnew []
,operator[]
et bien sûrdelete []
sont cuits. Cela peut être utilisé dans des conteneurs STL et pour autant que je sache, toutboost:shared_ptr
fait bien que vous ne puissiez pas utiliserboost::weak_ptr
avec ceux-ci. Cependant, vous pouvez également utiliser unboost::shared_ptr<std::vector<>>
pour une fonctionnalité similaire et pour retrouver la possibilité d'utiliserboost::weak_ptr
pour les références.boost::scoped_array
- Ceci est unboost::scoped_ptr
pour les tableaux. Comme pourboost::shared_array
tout le tableau nécessaire, la qualité du tableau est intégrée. Celui-ci n'est pas copiable et ne peut donc pas être utilisé dans des conteneurs STL. J'ai trouvé presque partout où vous souhaitez utiliser ce que vous pourriez probablement utiliserstd::vector
. Je n'ai jamais déterminé ce qui est en fait le plus rapide ou qui a moins de frais généraux, mais ce tableau à portée semble beaucoup moins impliqué qu'un vecteur STL. Lorsque vous souhaitez conserver l'allocation sur la pile, pensez à laboost::array
place.Qt
QPointer
- Introduit dans Qt 4.0, il s'agit d'un pointeur intelligent "faible" qui ne fonctionne qu'avec lesQObject
classes dérivées, ce qui dans le framework Qt est presque tout donc ce n'est pas vraiment une limitation. Cependant, il y a des limitations à savoir qu'il ne fournit pas de pointeur "fort" et bien que vous puissiez vérifier si l'objet sous-jacent est valide avecisNull()
vous pourriez trouver votre objet en cours de destruction juste après avoir passé cette vérification, en particulier dans les environnements multithreads. Qt les gens considèrent cela comme obsolète, je crois.QSharedDataPointer
- C'est un pointeur intelligent "fort" potentiellement comparable àboost::intrusive_ptr
bien qu'il ait une sécurité intégrée des threads mais il vous oblige à inclure des méthodes de comptage de références (ref
etderef
) que vous pouvez faire en sous-classantQSharedData
. Comme avec une grande partie de Qt, les objets sont mieux utilisés grâce à un héritage suffisant et le sous-classement de tout semble être la conception prévue.QExplicitlySharedDataPointer
- Très similaire àQSharedDataPointer
sauf qu'il n'appelle pas implicitementdetach()
. J'appellerais cette version 2.0 deQSharedDataPointer
car cette légère augmentation du contrôle quant au moment exact de se détacher après que le nombre de références tombe à zéro ne vaut pas particulièrement un tout nouvel objet.QSharedPointer
- Comptage de références atomiques, thread safe, pointeur partageable, suppressions personnalisées (prise en charge des tableaux), sonne comme tout ce qu'un pointeur intelligent devrait être. C'est ce que j'utilise principalement comme pointeur intelligent dans Qt et je le trouve comparable avecboost:shared_ptr
bien que probablement beaucoup plus de frais généraux comme de nombreux objets dans Qt.QWeakPointer
- Sentez-vous un schéma récurrent? Tout commestd::weak_ptr
etboost::weak_ptr
ceci est utilisé en conjonction avecQSharedPointer
lorsque vous avez besoin de références entre deux pointeurs intelligents qui, autrement, empêcheraient vos objets d'être supprimés.QScopedPointer
- Ce nom devrait également sembler familier et était en fait basé sur laboost::scoped_ptr
différence des versions Qt des pointeurs partagés et faibles. Il fonctionne pour fournir un pointeur intelligent à propriétaire unique sans que la surcharge leQSharedPointer
rend plus approprié pour la compatibilité, le code de sécurité d'exception et toutes les choses que vous pourriez utiliserstd::auto_ptr
ouboost::scoped_ptr
pour.la source
intrusive_ptr
est généralement plus efficace queshared_ptr
, car il vous permet d'avoir une seule allocation de tas par objet.shared_ptr
va dans le cas général allouer un petit objet de tas séparé pour les compteurs de référence.std::make_shared
peut être utilisé pour tirer le meilleur parti des deux mondes.shared_ptr
avec une seule allocation de tas.shared_ptr
s? (Sans compter la résolution des références cycliques)Il existe également Loki qui implémente des pointeurs intelligents basés sur des politiques.
Autres références sur les pointeurs intelligents basés sur des politiques, abordant le problème de la mauvaise prise en charge de l'optimisation de la base vide ainsi que de l'héritage multiple par de nombreux compilateurs:
la source
En plus de ceux donnés, il y en a aussi des axés sur la sécurité:
SaferCPlusPlus
mse::TRefCountingPointer
est un pointeur intelligent de comptage de référence commestd::shared_ptr
. La différence est qu'ilmse::TRefCountingPointer
est plus sûr, plus petit et plus rapide, mais ne possède aucun mécanisme de sécurité de filetage. Et il vient dans des versions «non nulles» et «fixes» (non reciblables) qui peuvent être supposées en toute sécurité pointer toujours vers un objet alloué valablement. Donc, fondamentalement, si votre objet cible est partagé entre des threads asynchronesstd::shared_ptr
, utilisez-le , sinonmse::TRefCountingPointer
c'est plus optimal.mse::TScopeOwnerPointer
est similaire àboost::scoped_ptr
, mais fonctionne en conjonction avecmse::TScopeFixedPointer
dans une relation de pointeur "fort-faible" du type commestd::shared_ptr
etstd::weak_ptr
.mse::TScopeFixedPointer
pointe vers des objets alloués sur la pile ou dont le pointeur "propriétaire" est alloué sur la pile. Il est (intentionnellement) limité dans ses fonctionnalités pour améliorer la sécurité au moment de la compilation sans coût d'exécution. Le but des pointeurs de "portée" est essentiellement d'identifier un ensemble de circonstances qui sont suffisamment simples et déterministes pour qu'aucun mécanisme de sécurité (d'exécution) ne soit nécessaire.mse::TRegisteredPointer
se comporte comme un pointeur brut, sauf que sa valeur est automatiquement définie sur null_ptr lorsque l'objet cible est détruit. Il peut être utilisé comme remplacement général des pointeurs bruts dans la plupart des situations. Comme un pointeur brut, il n'a aucune sécurité intrinsèque de thread. Mais en échange, il n'a aucun problème à cibler les objets alloués sur la pile (et à obtenir l'avantage de performance correspondant). Lorsque les vérifications d'exécution sont activées, ce pointeur ne peut pas accéder à la mémoire non valide. Parce qu'ilmse::TRegisteredPointer
a le même comportement qu'un pointeur brut lorsqu'il pointe vers des objets valides, il peut être "désactivé" (automatiquement remplacé par le pointeur brut correspondant) avec une directive de compilation, lui permettant d'être utilisé pour aider à détecter les bogues dans le débogage / test / modes bêta sans frais généraux en mode de sortie.Voici un article décrivant pourquoi et comment les utiliser. (Remarque, prise sans vergogne.)
la source