116 votes

Pointeurs, pointeurs intelligents ou pointeurs partagés ?

Je programme avec des pointeurs normaux, mais j'ai entendu parler de bibliothèques comme Boost qui implémentent des pointeurs intelligents. J'ai également vu que le moteur de rendu d'Ogre3D fait un usage intensif des pointeurs partagés.

Quelle est la différence exacte entre les trois, et dois-je m'en tenir à l'utilisation d'un seul type d'entre eux ?

146voto

hazzen Points 7315

Sydius a assez bien décrit les types :

  • Pointeurs normaux sont juste ça - ils pointent vers quelque chose dans la mémoire quelque part. Qui en est le propriétaire ? Seuls les commentaires vous le diront. Qui le libère ? Le propriétaire, espérons-le, à un moment donné.
  • Pointeurs intelligents sont un terme générique qui couvre de nombreux types ; je suppose que vous vouliez parler de pointeur scopé, qui utilise la fonction RAII modèle. C'est un objet alloué par la pile qui enveloppe un pointeur ; lorsqu'il sort de sa portée, il appelle delete sur le pointeur qu'il enveloppe. Il "possède" le pointeur contenu dans la mesure où il est chargé de le supprimer à un moment donné. Ils vous permettent d'obtenir une référence brute au pointeur qu'ils enveloppent pour le transmettre à d'autres méthodes, ainsi que de libérant le pointeur, permettant à quelqu'un d'autre de le posséder. Les copier n'a pas de sens.
  • Pointeurs partagés est un objet alloué par la pile qui enveloppe un pointeur de façon à ce que vous n'ayez pas à savoir qui en est le propriétaire. Lorsque le dernier pointeur partagé d'un objet en mémoire est détruit, le pointeur enveloppé est également supprimé.

Et quand faut-il les utiliser ? Vous ferez un usage intensif des pointeurs scopés ou des pointeurs partagés. Combien de threads sont en cours d'exécution dans votre application ? Si la réponse est "potentiellement beaucoup", les pointeurs partagés peuvent s'avérer être un goulot d'étranglement des performances s'ils sont utilisés partout. La raison en est que la création/copie/destruction d'un pointeur partagé doit être une opération atomique, ce qui peut nuire aux performances si vous avez de nombreux threads en cours d'exécution. Cependant, ce ne sera pas toujours le cas - seuls les tests vous le diront avec certitude.

Il y a un argument (que j'aime bien) contre les pointeurs partagés - en les utilisant, vous permettez aux programmeurs d'ignorer qui possède un pointeur. Cela peut conduire à des situations délicates avec des références circulaires (Java les détecte, mais pas les pointeurs partagés) ou à la paresse générale des programmeurs dans une grande base de code.

Il y a deux raisons d'utiliser les pointeurs scopés. La première est pour la sécurité des exceptions simples et les opérations de nettoyage - si vous voulez garantir qu'un objet est nettoyé quoi qu'il arrive face aux exceptions, et que vous ne voulez pas allouer cet objet en pile, mettez-le dans un pointeur scoped. Si l'opération est un succès, vous pouvez le transférer sur un pointeur partagé, mais en attendant, économisez l'overhead avec un pointeur scopé.

L'autre cas est celui où l'on veut une propriété claire des objets. Certaines équipes préfèrent cela, d'autres non. Par exemple, une structure de données peut renvoyer des pointeurs vers des objets internes. Avec un pointeur scopé, elle renvoie un pointeur brut ou une référence qui doit être traitée comme une référence faible - c'est une erreur d'accéder à ce pointeur après que la structure de données qui le possède ait été détruite, et c'est une erreur de le supprimer. Dans le cas d'un pointeur partagé, l'objet propriétaire ne peut pas détruire les données internes qu'il a renvoyées si quelqu'un détient toujours un handle dessus - cela pourrait laisser des ressources ouvertes pendant beaucoup plus longtemps que nécessaire, ou bien pire selon le code.

33voto

Le terme "pointeur intelligent" comprend pointeurs partagés, pointeurs automatiques, pointeurs de verrouillage et autres. vous avez voulu dire pointeur automatique (plus connu sous le nom ambigu de "pointeur propriétaire"), et non pointeur intelligent.

Les pointeurs muets (T*) ne sont jamais la meilleure solution. Ils vous obligent à gérer explicitement la mémoire, ce qui est verbeux, source d'erreurs et parfois presque impossible. Mais plus important encore, ils ne signalent pas votre intention.

Les pointeurs automatiques suppriment le pointeur lors de sa destruction. Pour les tableaux, préférez les encapsulations comme vector et deque. Pour les autres objets, il y a très rarement besoin de les stocker sur le tas - il suffit d'utiliser les locals et la composition d'objets. Le besoin de pointeurs automatiques se fait toujours sentir avec les fonctions qui renvoient des pointeurs de tas -- comme les fabriques et les retours polymorphes.

Les pointeurs partagés suppriment le pointeur lorsque le dernier pointeur partagé vers celui-ci est détruit. C'est utile lorsque vous voulez un système de stockage sans limite, où la durée de vie attendue et la propriété peuvent varier considérablement en fonction de la situation. En raison de la nécessité de conserver un compteur (atomique), ils sont un peu plus lents que les pointeurs automatiques. Certains disent, à moitié en plaisantant, que les pointeurs partagés sont pour les personnes qui ne savent pas concevoir des systèmes - jugez par vous-même.

Pour une contrepartie essentielle aux pointeurs partagés, consultez également les pointeurs faibles.

21voto

Sydius Points 3656

Les pointeurs intelligents se nettoient eux-mêmes lorsqu'ils sortent de leur champ d'application (ce qui élimine la crainte de la plupart des fuites de mémoire). Les pointeurs partagés sont des pointeurs intelligents qui tiennent un compte du nombre d'instances du pointeur existant, et ne nettoient la mémoire que lorsque le compte atteint zéro. En général, n'utilisez que des pointeurs partagés (mais veillez à utiliser le bon type - il en existe un différent pour les tableaux). Ils ont beaucoup à voir avec RAII .

10voto

d0k Points 1890

Pour éviter les fuites de mémoire, vous pouvez utiliser des pointeurs intelligents chaque fois que vous le pouvez. Il existe deux types de pointeurs intelligents en C++

  • Référence comptée (par exemple boost::shared_ptr / std::tr1:shared_ptr)
  • comptés sans référence (par exemple boost::scoped_ptr / std::auto_ptr)

La principale différence est que les pointeurs intelligents comptés par référence peuvent être copiés (et utilisés dans des conteneurs std: :) alors que les scoped_ptr ne le peuvent pas. Les pointeurs non comptés par référence n'ont presque pas de surcharge ou pas de surcharge du tout. Le comptage de référence introduit toujours un certain type de surcharge.

(Je suggère d'éviter auto_ptr, il a quelques défauts sérieux s'il est utilisé incorrectement)

5voto

Shane MacLaughlin Points 12765

Pour ajouter un peu à la réponse de Sydius, les pointeurs intelligents fourniront souvent une solution plus stable en attrapant de nombreuses erreurs faciles à faire. Les pointeurs bruts ont des avantages en termes de performance et peuvent être plus flexibles dans certaines circonstances. Vous pouvez également être contraint d'utiliser des pointeurs bruts lors de la liaison avec certaines bibliothèques tierces.

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