Quelles sont les implémentations de pointeur intelligent C++ disponibles?

comparaisons, Avantages, inconvénients, et quand utiliser?

C'est un spin-off d'un fil de collecte des ordures où ce que je pensais être une réponse simple a généré beaucoup de commentaires sur certaines implémentations smart pointer spécifiques donc il semblait intéressant de commencer un nouveau post.

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 des avantages et des inconvénients simples ou des exceptions et des gotchas à quelque chose que vous pourriez autrement penser devrait fonctionner.

j'ai posté quelques implémentations que j'ai utilisées ou au moins passées sous silence 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 exactes à 100% alors n'hésitez pas à me vérifier les faits ou à me corriger au besoin.

le but est d'apprendre quelques nouveaux objets et bibliothèques ou corriger mon usage et compréhension des implémentations existantes déjà largement utilisées et finissent avec une référence décente pour les autres.

115
demandé sur Community 2011-02-17 10:29:54

3 réponses

C++03

std::auto_ptr - peut-être l'un des originaux souffrait-il du syndrome du premier jet ne fournissant que des installations limitées de collecte des ordures. Le premier inconvénient est qu'il appelle delete lors de la destruction, ce qui les rend inacceptables pour tenir les objets alloués tableau ( new[] ). Il prend la propriété du pointeur de sorte que deux pointeurs automatiques ne devraient pas contenir le même objet. L'attribution de transférez la propriété et réinitialisez le pointeur automatique rvalue à un pointeur nul. Ce qui conduit peut-être au pire inconvénient; ils ne peuvent pas être utilisés dans les conteneurs STL en raison de l'incapacité susmentionnée à être copié. Le coup final à tout cas d'utilisation est qu'ils sont destinés à être dépréciés 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 avec std::auto_ptr pour permettre la copie et affectation dans certaines situations. Plus précisément, il peut être utilisé pour convertir un non-const std::auto_ptr en un lvalue en utilisant le tour Colvin-Gibbons également connu sous le nom de move constructor pour transférer la propriété.

au contraire peut-être std::auto_ptr n'était pas vraiment destiné à être utilisé comme un pointeur intelligent à usage général pour la collecte automatique des ordures. La plupart de ma compréhension limitée et des hypothèses sont basées sur utilisation efficace par Herb Sutter de auto_ptr 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 remplacer std::auto_ptr il sera tout à fait similaire sauf avec les améliorations clés pour corriger les faiblesses de std::auto_ptr comme travailler avec des tableaux, lvalue la protection via un constructeur de copie privée, utilisable avec des conteneurs STL et les algorithmes, etc. Étant donné que ses performances aériennes et son empreinte mémoire sont limitées, il s'agit d'un candidat idéal pour remplacer, ou peut-être mieux décrit comme possédant, des pointeurs bruts. Comme le" unique " implique qu'il n'y a qu'un seul propriétaire du pointeur comme le précédent std::auto_ptr .

std::shared_ptr - je crois que c'est basé sur TR1 et boost::shared_ptr mais amélioré pour inclure l'aliasing et l'arithmétique pointeur ainsi. En bref, il enveloppe une référence compté pointeur intelligent autour d'un objet alloué dynamiquement. Comme le "partagé" implique que le pointeur peut être la propriété de plus d'un pointeur partagé lorsque la dernière référence du dernier pointeur partagé sort de la portée alors l'objet sera supprimé de manière appropriée. Ceux-ci sont également thread safe et peut traiter les types incomplets dans la plupart des cas. std::make_shared peut être utilisé efficacement construire un std::shared_ptr avec une allocation de tas en utilisant l'allocateur par défaut.

std::weak_ptr - également basé sur TR1 et boost::weak_ptr . C'est une référence à un objet appartenant à un std::shared_ptr et ne seront donc pas empêcher la suppression de l'objet, si le std::shared_ptr référence compteur tombe à zéro. Pour accéder au pointeur brut, vous devez d'abord accéder au std::shared_ptr en appelant le lock qui retourner un std::shared_ptr vide si le pointeur a expiré et a déjà été détruit. Ceci est principalement utile pour éviter les comptes de référence suspendus indéfinis lors de l'utilisation de plusieurs pointeurs intelligents.


Boost

boost::shared_ptr - probablement le plus facile à utiliser dans les scénarios les plus variés (STL, PIMPL, RAII, etc) il s'agit d'un pointeur smart counted référencé partagé. J'ai entendu quelques plaintes au sujet de la performance et des frais généraux dans certaines situations, mais j'ai dû les ignorer parce que je ne me souviens pas de quelle dispute il s'agissait. Apparemment, il était assez populaire pour devenir un objet standard C++ 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 de std::weak_ptr , basée sur cette implémentation, cela permet une référence non-propriétaire à un boost::shared_ptr . Vous appelez sans surprise lock() pour accéder au pointeur partagé" fort " et devez vérifier pour s'assurer qu'il est valide car il aurait déjà pu être détruit. Assurez-vous juste de ne pas stocker le pointeur partagé retourné et le laisser sortir de la portée dès que vous en avez fini avec lui sinon vous êtes de retour au problème de référence cyclique où vos comptes de référence seront suspendus et les objets ne seront pas détruits.

boost::scoped_ptr - Il s'agit d'une classe de pointeur intelligent simple avec peu au-dessus probablement conçu pour une alternative plus performante à boost::shared_ptr lorsqu'il est utilisable. Il est comparable à std::auto_ptr surtout dans le fait qu'il ne peut pas être utilisé en toute sécurité comme un é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 de ma compréhension il est conçu pour être utilisé lors de la création de votre propre pointeur intelligent compatible classes. Vous devez implémenter la référence en comptant vous-même, vous aurez également besoin d'implémenter quelques méthodes si vous voulez que votre classe soit générique, de plus vous devrez implémenter votre propre sécurité de thread. Du côté positif, cela vous donne probablement la façon la plus personnalisée de choisir et de choisir exactement combien ou combien peu "intelligibilité" vous voulez. intrusive_ptr est généralement plus efficace que shared_ptr car il vous permet d'avoir une seule allocation de tas par objet. (merci Arvid)

boost::shared_array - C'est un boost::shared_ptr pour les tableaux. Fondamentalement, new [] , operator[] , et bien sûr delete [] sont cuites. Cela peut être utilisé dans les conteneurs STL et pour autant que je sache fait tout boost:shared_ptr fait bien que vous ne pouvez pas utiliser boost::weak_ptr avec ceux-ci. Vous pouvez toutefois utiliser une alternative boost::shared_ptr<std::vector<>> pour des fonctionnalités similaires et de retrouver la possibilité d'utiliser boost::weak_ptr pour référence.

boost::scoped_array - C'est un boost::scoped_ptr pour les tableaux. Comme avec boost::shared_array toute la bonté de tableau nécessaire est cuit dans. Celui-ci est non-copiable et ne peut donc pas être utilisé dans les conteneurs STL. J'ai trouvé presque partout où vous vous trouvez voulant utiliser ce que vous pourriez probablement juste utiliser std::vector . Je n'ai jamais déterminé qui est en fait plus rapide ou a moins de frais généraux, mais ce tableau scoped semble beaucoup moins impliqué qu'un STL vecteur. Quand vous voulez garder l'allocation sur la pile considèrent boost::array à la place.


Qt

QPointer - introduit dans Qt 4.0 il s'agit d'un pointeur intelligent "faible" qui ne fonctionne qu'avec QObject et les classes dérivées, qui dans le cadre de Qt est presque tout donc ce n'est pas vraiment une limitation. Cependant, il existe limitations à savoir qu'il ne fournit pas un pointeur "fort" et bien que vous puissiez vérifier si l'objet sous-jacent est valide avec isNull() vous pourriez trouver votre objet en cours de destruction juste après que vous passiez cette vérification en particulier dans les environnements multi-threadés. Je crois que les gens du Qt considèrent que c'est déprécié.

QSharedDataPointer - il s'agit d'un pointeur intelligent "fort" potentiellement comparable à boost::intrusive_ptr bien qu'il ait un certain intégré dans la sécurité de filetage mais il exige que vous incluiez des méthodes de comptage de référence ( ref et deref ) que vous pouvez faire en sous-classant QSharedData . Comme avec une grande partie de Qt les objets sont mieux utilisés à travers l'héritage ample et sous-classant tout semble être la conception prévue.

QExplicitlySharedDataPointer - très similaire à QSharedDataPointer sauf qu'il n'appelle pas implicitement detach() . J'appellerais cette version 2.0 de QSharedDataPointer comme cette légère l'augmentation du contrôle quant au moment exact où détacher après le compte de référence baisse à zéro ne vaut pas particulièrement un tout nouvel objet.

QSharedPointer - le comptage de référence atomique, le fil sûr, le pointeur partageable, les suppressions personnalisées (le soutien de tableau), sonne comme tout un pointeur intelligent devrait être. C'est ce que j'utilise principalement comme un pointeur intelligent dans Qt et je le trouve comparable à boost:shared_ptr bien que probablement beaucoup plus de frais généraux comme beaucoup d'objets dans Qt.

QWeakPointer - ressentez-vous un schéma récurrent? Tout comme std::weak_ptr et boost::weak_ptr il est utilisé en conjonction avec QSharedPointer lorsque vous avez besoin de références entre deux pointeurs intelligents qui, autrement, causeraient vos objets à ne jamais être supprimés.

QScopedPointer - ce nom devrait aussi sembler familier et était en fait basé sur boost::scoped_ptr contrairement aux versions Qt des pointeurs partagés et faibles. Il fonctionne pour fournir un pointeur intelligent propriétaire unique sans le plafond de QSharedPointer qui le rend plus adapté à la compatibilité, exception code de sécurité, et toutes les choses que vous pourriez utiliser std::auto_ptr ou boost::scoped_ptr pour.

221
répondu AJG85 2013-03-25 21:20:38

il y a aussi Loki qui implémente des pointeurs intelligents basés sur les politiques.

autres références sur les indicateurs intelligents basés sur les politiques, abordant le problème du faible soutien de l'optimisation de base vide avec héritage multiple par de nombreux compilateurs:

5
répondu Gregory Pakosz 2011-09-02 23:42:51

en plus de ceux qui sont donnés, il y a aussi ceux qui sont orientés vers la sécurité:

Safercpluslus

mse::TRefCountingPointer est un pointeur intelligent de référence comme std::shared_ptr . La différence étant que mse::TRefCountingPointer est plus sûr, plus petit et plus rapide, mais n'a pas de fil mécanisme de sécurité. Et il est livré dans des versions" non null "et " fixe" (Non-re - valablement objet alloué. Donc, si votre objet cible est partagé entre des threads asynchrones, utilisez std::shared_ptr , sinon mse::TRefCountingPointer est plus optimal.

mse::TScopeOwnerPointer est similaire à boost::scoped_ptr , mais fonctionne en conjonction avec mse::TScopeFixedPointer dans une "forte-faible" relation pointeur genre de comme std::shared_ptr et std::weak_ptr .

mse::TScopeFixedPointer points aux objets qui sont attribués sur la pile, ou dont le pointeur "propriétaire" est attribué sur la pile. Il est (intentionnellement) limité dans ses fonctionnalités pour améliorer la sécurité de la compilation sans coût d'exécution. Le but des indicateurs de "portée" est essentiellement d'identifier un ensemble de circonstances qui sont assez 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étruite. Il peut être utilisé comme un remplacement général pour pointeurs bruts dans la plupart des situations. Comme un pointeur brut, il n'a pas de sécurité intrinsèque de fil. 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 est à l'abri d'accéder à la mémoire invalide. Parce que mse::TRegisteredPointer a le même comportement qu'un pointeur brut lors du pointage d'objets, il peut être "désactivé" (automatiquement remplacé par le correspondant pointeur brut) avec un la directive compiler-time, qui permet de l'utiliser pour attraper les bogues en mode debug/test/beta sans frais généraux en mode release.

Ici est un article qui décrit pourquoi et comment les utiliser. (Note, plug effrontée.)

1
répondu Noah 2016-04-22 23:22:09