Ok, donc la dernière fois que j’ai écrit le C++ pour gagner ma vie, std::auto_ptr
était tout ce que la lib std avait à disposition, et boost::shared_ptr
était à la mode. Je n'ai jamais vraiment examiné les autres types de pointeurs intelligents boost fournis. Je comprends que C++ 11 fournit à présent certains des types que boost a proposés, mais pas tous.
Quelqu'un a-t-il donc un algorithme simple pour déterminer quand utiliser quel pointeur intelligent? Y compris de préférence des conseils concernant les pointeurs stupides (pointeurs bruts comme T*
) et le reste des pointeurs intelligents boost. (Quelque chose comme this serait génial).
Propriété partagée:
Le shared_ptr
et weak_ptr
la norme adoptée est à peu près la même que leur contrepartie de Boost . Utilisez-les lorsque vous avez besoin de partager une ressource sans savoir laquelle sera la dernière à être en vie. Utilisation weak_ptr
observer la ressource partagée sans en influencer la durée de vie, pas pour rompre les cycles. Cycles avec shared_ptr
ne devrait normalement pas se produire - deux ressources ne peuvent pas se posséder.
Notez que Boost propose en outre shared_array
, qui pourrait constituer une alternative appropriée à shared_ptr<std::vector<T> const>
.
Ensuite, les offres Boost intrusive_ptr
, qui constituent une solution légère si votre ressource offre déjà une gestion à comptage de références et que vous souhaitez l’adapter au principe RAII. Celui-ci n'a pas été adopté par la norme.
Propriété unique:
Boost a aussi un scoped_ptr
, qui n’est pas copiable et pour lequel vous ne pouvez pas spécifier de suppresseur. 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 template et est movable, contrairement à boost::scoped_ptr
. Il est également pleinement utilisable dans les conteneurs STL tant que vous n'utilisez pas d'opérations nécessitant des types copiables (évidemment).
Notez à nouveau que Boost a une version tableau: scoped_array
, que le standard a unifié en exigeant std::unique_ptr<T[]>
spécialisation partielle qui va delete[]
le pointeur au lieu de delete
ing (avec le default_delete
r). std::unique_ptr<T[]>
offre également operator[]
au lieu de operator*
et operator->
.
Notez que std::auto_ptr
est toujours dans la norme, mais c'est obsolète. §D.10 [depr.auto.ptr]
Le modèle de classe
auto_ptr
est obsolète. [ Note: Le modèle de classeunique_ptr
(20.7.1) fournit une meilleure solution. — note de fin]
Aucune propriété:
Utilisez des pointeurs muets (pointeurs bruts) ou des références pour des références non propriétaires aux ressources et lorsque vous savez que le la ressource survivra à l'objet/à la portée de référence. Préférez les références et utilisez des pointeurs bruts lorsque vous avez besoin de la nullité ou de la réinitialisation.
Si vous voulez une référence non propriétaire à une ressource, mais que vous ne savez pas si la ressource survivra à l'objet qui la référence, placez la ressource dans un shared_ptr
et utilisez un weak_ptr
- vous pouvez tester si le parent shared_ptr
est en vie avec lock
, qui retournera un shared_ptr
qui est non nul si la ressource existe toujours. Si vous voulez tester si la ressource est morte, utilisez expired
. Les deux peuvent sembler similaires, mais sont très différentes en cas d’exécution simultanée, car expired
ne garantit que sa valeur de retour pour cette seule instruction. Un test apparemment innocent comme
if(!wptr.expired())
something_assuming_the_resource_is_still_alive();
est une condition de concurrence potentielle.
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 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 objet Person
contrôle la durée de vie d'un objet House
. La mise en conflit de ces concepts du monde réel avec des concepts logiciels est un moyen 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éfinissez une "direction" et utilisez std::shared_ptr<T>
dans un sens et std::weak_ptr<T>
dans l'autre.
Si l'objet vous possède, mais qu'il est possible que vous n'ayez pas de propriétaire, utilisez les pointeurs normaux T*
(par exemple, les pointeurs parents).
Si l'objet vous possède (ou a une existence garantie), utilisez les références T&
.
Mise en garde: Soyez conscient des coûts des pointeurs intelligents. Dans les environnements limités en mémoire ou en performances, il pourrait être avantageux d'utiliser uniquement 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érences sur la copie, plus un décrément lors de la destruction suivi d'un contrôle de comptage zéro avec suppression de l'objet maintenu. Selon l’implémentation, cela peut alourdir votre code et entraîner des problèmes de performances.Exemples:
struct BinaryTree
{
Tree* m_parent;
std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};
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 ceux-ci sont std::unique_ptr
.
struct ListNode
{
std::shared_ptr<ListNode> m_next;
std::weak_ptr<ListNode> m_prev;
};
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 next et weak_ptr
pour prev pour briser le cycle.
Utilisez unique_ptr<T>
Tout le temps sauf lorsque vous avez besoin de compter les références, auquel cas utilisez shared_ptr<T>
(Et dans de très rares cas, weak_ptr<T>
Pour empêcher les cycles de référence). Dans presque tous les cas, la propriété unique transférable convient parfaitement.
Pointeurs bruts: bons uniquement si vous avez besoin de retours covariants, le pointage non propriétaire pouvant se produire. Ils ne sont pas terriblement utiles autrement.
Pointeurs sur les tableaux: unique_ptr
A une spécialisation pour T[]
Qui appelle automatiquement delete[]
Sur le résultat. Vous pouvez ainsi effectuer unique_ptr<int[]> p(new int[42]);
en toute sécurité, par exemple. shared_ptr
Vous aurez toujours besoin d'un deleter personnalisé, mais vous n'avez pas besoin d'un pointeur de tableau partagé ou unique. Bien sûr, il est préférable de remplacer de telles choses par std::vector
De toute façon. Malheureusement, shared_ptr
Ne fournit pas de fonction d'accès aux tableaux, vous devez donc toujours appeler manuellement get()
, mais unique_ptr<T[]>
Fournit operator[]
Au lieu de operator*
Et operator->
. Dans tous les cas, vous devez vous contrôler. Cela rend shared_ptr
Légèrement moins convivial, bien que l'avantage générique et l'absence de dépendance Boost rendent à nouveau unique_ptr
Et shared_ptr
.
Pointeurs Scoped: rendus inutiles par unique_ptr
, Tout comme auto_ptr
.
Il n'y a vraiment rien de plus. 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, tels que intrusive_ptr
Ou interprocess_ptr
. Cependant, ils sont très niches et totalement inutiles dans le cas général.
Cas d'utilisation de unique_ptr
:
Cas d'utilisation de shared_ptr
:
Cas d'utilisation de weak_ptr
:
N'hésitez pas à éditer et ajouter plus