235 votes

Quel genre de pointeur dois-je utiliser quand?

Ok, donc la dernière fois que j'ai écrit en C++ pour un vivant, std::auto_ptr a toutes les mst lib disponibles, et boost::shared_ptr a été à la mode. Je n'ai jamais vraiment regardé dans l'autre pointeur intelligent types d'achats. Je comprends que C++11 fournit maintenant quelques-uns des types de boost est venu avec, mais pas tous d'entre eux.

Ainsi, quelqu'un avoir un algorithme simple pour déterminer quand utiliser de pointeur intelligent? De préférence, y compris des conseils sur muet pointeurs (raw pointeurs comme T*) et le reste de la poussée des pointeurs intelligents. (Quelque chose comme ce serait génial).

185voto

Xeo Points 69818

Le partage de la propriété:
L' shared_ptr et weak_ptr la norme qui est adoptée sont à peu près les mêmes que les Boost homologues. Utilisez-la lorsque vous avez besoin de partager une ressource et ne sait pas ce qui sera le dernier à être encore en vie. Utiliser weak_ptr d'observer la ressource partagée, sans influence sur sa durée de vie, de ne pas briser les cycles. Avec des Cycles de shared_ptr ne devrait normalement pas se produire - deux ressources ne pouvez pas posséder les uns des autres.

Notez que Boost dispose aussi d' shared_array, ce qui pourrait être une alternative appropriée à l' shared_ptr<std::vector<T> const>.

Ensuite, de Stimuler l'offre intrusive_ptr, qui sont une solution légère et si votre ressource offre de référence compte de gestion déjà et que vous souhaitez l'adopter pour le RAII principe. Cette une n'a pas été adopté par la norme.

Unique propriété:
Boost a aussi un scoped_ptr, ce qui n'est pas copiable et pour laquelle vous ne pouvez pas spécifier un deleter. std::unique_ptr est boost::scoped_ptr sur les 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 deleter dans ses arguments de modèle et est mobile, contrairement à l' boost::scoped_ptr. Il est également utilisable dans des conteneurs STL tant que vous n'utilisez pas les opérations qui doivent copiable types (évidemment).

A noter encore que Boost est un tableau version: scoped_array,, la norme unifiée en exigeant std::unique_ptr<T[]> partielle de la spécialisation qui sera delete[] le pointeur au lieu de deleteing (avec l' default_deleter). std::unique_ptr<T[]> propose également operator[] au lieu de operator* et operator->.

Notez que std::auto_ptr est encore dans la norme, mais il est obsolète. §D.10 [depr.auto.ptr]

Le modèle de classe auto_ptr est obsolète. [ Note: Le modèle de classe unique_ptr (20.7.1) fournit une meilleure solution. -la note de fin ]

Aucun droit de propriété:
Utilisation muet pointeurs (raw pointeurs) ou des références pour les non-posséder des références à des ressources et quand on sait que la ressource va survivre à l'objet de référence / domaine d'application. Préférez les références et utiliser des pointeurs lorsque vous avez besoin de la possibilité de valeur null ou resettability.

Si vous voulez un non propriétaire de référence à une ressource, mais vous ne savez pas si la ressource va survivre à l'objet de référence, pack la ressource en shared_ptr et l'utilisation d'un weak_ptr - vous pouvez tester si le parent shared_ptr est vivant avec des lock, qui sera de retour une shared_ptr qui est non-nulle si la ressource existe toujours. Si vous voulez tester si la ressource est morte, utilisez expired. Les deux se ressemblent, mais sont très différentes face à une exécution simultanée, expired seulement des garanties de valeur de retour pour cette seule déclaration. Une apparence innocente, comme le test d'

if(!wptr.expired())
  something_assuming_the_resource_is_still_alive();

est un potentiel à condition de course.

127voto

Peter Alexander Points 31990

Décider de ce pointeur intelligent à utiliser est une question de propriété. Quand il s'agit de la gestion des ressources, l'objet A est propriétaire de l'objet B si elle est dans le contrôle de la durée de vie de l'objet B. Par exemple, les variables de membre sont la propriété de leurs objets respectifs, car la durée de vie des variables de membre est liée à la durée de vie de l'objet. Vous choisissez des pointeurs intelligents basés sur la façon dont l'objet est la propriété.

Notez que la propriété dans un système de logiciel est séparée de la propriété que nous pensons de lui en dehors du logiciel. Par exemple, une personne pourrait "propre" de leur maison, mais cela ne veut pas nécessairement dire que l' Person objet a le contrôle sur la durée de vie d'un House objet. L'amalgame entre ces monde réel concepts avec le logiciel de concepts est un moyen infaillible pour programmer vous-même dans un trou.


Si vous avez l'entière propriété de l'objet, utilisez std::unique_ptr<T>.

Si vous avez partagé la propriété de l'objet...
- Si il n'y a pas de cycles dans la propriété, l'utilisation std::shared_ptr<T>.
- Si il y a des cycles, de définir une "direction" et de l'utiliser std::shared_ptr<T> dans un sens et std::weak_ptr<T> dans l'autre.

Si l'objet possède vous, mais il est possible de ne pas avoir de propriétaire, de l'utilisation normale des pointeurs T* (des parents, par exemple les pointeurs).

Si l'objet possède vous (ou sinon a garanti l'existence), utiliser des références T&.


Mise en garde: prendre en compte le coût des pointeurs intelligents. Dans la mémoire ou les performances limitées des environnements, il pourrait être bénéfique à juste normal d'utilisation de pointeurs avec un manuel du système de gestion de la mémoire.

Les coûts:

  • Si vous avez une coutume deleter (par exemple, vous utilisez l'allocation des piscines), alors cela entraînera des frais généraux par pointeur qui peut être facilement évitée par la suppression manuelle.
  • std::shared_ptr a l'aide d'un compteur de référence de l'incrément sur la copie, en plus d'une diminution de la destruction suivi d'un 0-comte vérifier avec la suppression de l'objet. En fonction de la mise en œuvre, ce qui peut gonfler votre code et provoquer des problèmes de performances.
  • Moment de la compilation. Comme avec tous les modèles, des pointeurs intelligents contribuent négativement à des temps de compilation.

Exemples:

struct BinaryTree
{
    Tree* m_parent;
    std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};

Un arbre binaire n'est pas propriétaire de son parent, mais l'existence d'un arbre implique l'existence de ses parents (ou nullptr pour les racines), de sorte qu'utilise un pointeur normal. Un arbre binaire (avec la valeur sémantique) est la propriété exclusive de ses enfants, de sorte que ceux-ci sont std::unique_ptr.

struct ListNode
{
    std::shared_ptr<ListNode> m_next;
    std::weak_ptr<ListNode> m_prev;
};

Ici, la liste nœud possède son prochain et listes précédentes, nous avons donc définir une direction et d'utilisation shared_ptr pour la prochaine et weak_ptr pour les prev de briser le cycle.

20voto

Puppy Points 90818

Utiliser unique_ptr<T> tout le temps, sauf quand vous avez besoin de comptage de référence, auquel cas utiliser shared_ptr<T> (et pour des cas très rares, weak_ptr<T> pour éviter des cycles de référence). Dans presque tous les cas, transférable unique propriété est tout simplement parfait.

Raw pointeurs: Bon si vous avez besoin d'covariante renvoie, non propriétaire de pointage qui peut arriver. Ils ne sont pas terriblement utile autrement.

Tableau de pointeurs: unique_ptr a une spécialisation pour T[] qui appelle automatiquement delete[] sur le résultat, de sorte que vous pouvez faire en toute sécurité unique_ptr<int[]> p(new int[42]); par exemple. shared_ptr vous auriez encore besoin d'un custom deleter, mais vous n'avez pas besoin d'une institution spécialisée ou partagée unique pointeur sur le tableau. Bien sûr, ces choses qui sont généralement la meilleure remplacé par std::vector de toute façon. Malheureusement, shared_ptr ne fournissent pas un accès au tableau de la fonction, de sorte que vous auriez toujours manuellement l'appel get(), mais unique_ptr<T[]> fournit operator[] au lieu de operator* et operator->. Dans tous les cas, vous devez les limites de vérifier vous-même. Cela rend shared_ptr un peu moins convivial, bien que sans doute le générique avantage et pas de Stimuler la dépendance unique_ptr et shared_ptr les gagnants de nouveau.

L'étendue des pointeurs: Fait hors de propos en unique_ptr, tout comme auto_ptr.

Il n'y a vraiment rien de plus. En C++03, sans déplacer la sémantique de cette situation a été très compliqué, mais en C++11 le conseil est très simple.

Il y a encore utilise pour les autres pointeurs intelligents, comme intrusive_ptr ou interprocess_ptr. Cependant, ils sont très niche et totalement inutile dans le cas général.

9voto

Lalaland Points 4383

Les cas d'utilisation de unique_ptr:

  • Méthodes de fabrique
  • Les membres qui sont des pointeurs(pimpl inclus)
  • Stocke des pointeurs dans la stl containters(pour éviter de le déplacer)
  • L'utilisation de grands objets dynamiques

Les cas d'utilisation de shared_ptr:

  • Le partage d'objets entre threads
  • De liaison ou de capturer des pointeurs(utilisation de lambda ou std::bind)
  • Le partage d'objets en général
  • Personnalisé deleters

Les cas d'utilisation de weak:

  • Grande carte qui agit comme une référence générale(ex une carte de tous les sockets)

N'hésitez pas à modifier et ajouter plus de

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X