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 dans le moteur de rendu Ogre3D, il y a une utilisation profonde des pointeurs partagés.

Quelle est exactement la différence entre les trois et devrais-je continuer à en utiliser un seul type?

Sydius a bien décrit les types:

  • Les pointeurs normaux ne sont que cela – ils indiquent quelque chose en mémoire quelque part. Qui le possède? Seuls les commentaires vous le feront savoir. Qui le libère? J’espère que le propriétaire à un moment donné.
  • Les pointeurs intelligents sont un terme général qui couvre de nombreux types; Je suppose que vous voulez dire pointeur pointu qui utilise le motif RAII . C’est un object alloué à la stack qui enveloppe un pointeur; quand il est hors de scope, il appelle delete sur le pointeur qu’il enveloppe. Il “possède” le pointeur contenu en ce sens qu’il est chargé de le supprimer à un moment donné. Ils vous permettent d’obtenir une référence brute au pointeur qu’ils renvoient pour passer à d’autres méthodes, ainsi que de libérer le pointeur, permettant ainsi à quelqu’un d’autre de le posséder. Leur copie n’a pas de sens.
  • Les pointeurs partagés sont un object alloué à la stack qui encapsule un pointeur pour que vous n’ayez pas à savoir à qui il appartient. Lorsque le dernier pointeur partagé pour un object en mémoire est détruit, le pointeur encapsulé sera également supprimé.

Que diriez-vous quand vous devriez les utiliser? Vous allez soit faire un usage intensif de pointeurs ou de 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 se révéler être un goulot d’étranglement s’ils sont utilisés partout. La raison étant que la création / copie / destruction d’un pointeur partagé doit être une opération atomique, ce qui peut nuire aux performances si de nombreux threads sont en cours d’exécution. Cependant, ce ne sera pas toujours le cas – seuls les tests vous le diront à coup sûr.

Il y a un argument (que j’aime bien) contre les pointeurs partagés – en les utilisant, vous permettez aux programmeurs d’ignorer à qui appartient un pointeur. Cela peut conduire à des situations délicates avec des références circulaires (Java les détectera, mais les pointeurs partagés ne le peuvent pas) ou la paresse générale du programmeur dans une base de code volumineuse.

Il y a deux raisons d’utiliser des pointeurs ciblés. La première concerne les opérations simples de sécurité et de nettoyage des exceptions: si vous voulez garantir qu’un object est nettoyé, peu importe les exceptions, et que vous ne voulez pas emstackr cet object, placez-le dans un pointeur. Si l’opération réussit, vous pouvez le transférer sur un pointeur partagé, mais en attendant, enregistrez le surcoût avec un pointeur de scope.

L’autre cas est celui où vous souhaitez supprimer la propriété de l’object. Certaines équipes préfèrent cela, d’autres non. Par exemple, une structure de données peut renvoyer des pointeurs vers des objects internes. Sous un pointeur de scope, il renverrait un pointeur ou une référence brute qui devrait être considéré comme une référence faible. L’access à ce pointeur après la structure de données qui le possède est une erreur et sa suppression est une erreur. Sous un pointeur partagé, l’object propriétaire ne peut pas détruire les données internes qu’il a renvoyées si une personne l’a toujours en main – cela pourrait laisser les ressources ouvertes beaucoup plus longtemps que nécessaire, ou bien pire selon le code.

le terme “pointeur intelligent” inclut des pointeurs partagés, des pointeurs automatiques, des pointeurs de locking et autres. vous vouliez dire un pointeur automatique (plus connu sous le nom de “pointeur propriétaire”), pas un pointeur intelligent.

Les pointeurs (T *) ne sont jamais la meilleure solution. Ils vous font faire une gestion explicite de la mémoire, qui est verbeuse, sujette aux erreurs et parfois presque impossible. Mais plus important encore, ils ne signalent pas votre intention.

Les pointeurs automatiques suppriment la pointe à la destruction. Pour les tableaux, préférez les encapsulations comme vector et deque. Pour les autres objects, il est très rare de devoir les stocker sur le tas – utilisez simplement les sections locales et la composition des objects. Le besoin de pointeurs automatiques se pose toujours avec les fonctions qui renvoient des pointeurs de tas, tels que les fabriques et les retours polymorphes.

Les pointeurs partagés suppriment la pointe lorsque le dernier pointeur partagé est détruit. Ceci est utile lorsque vous voulez un schéma de stockage ouvert et transparent, où la durée de vie et la propriété attendues peuvent varier considérablement selon la situation. En raison de la nécessité de garder un compteur (atomique), ils sont un peu plus lents que les pointeurs automatiques. Certains disent à moitié que les pointeurs partagés sont pour les personnes qui ne peuvent pas concevoir de systèmes – jugez par vous-même.

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

Les pointeurs intelligents se nettoient eux-mêmes une fois qu’ils sont hors de scope (éliminant ainsi la peur de la plupart des memory leaks). Les pointeurs partagés sont des pointeurs intelligents qui tiennent compte du nombre d’instances du pointeur et ne nettoient la mémoire que lorsque le nombre atteint zéro. En général, utilisez uniquement des pointeurs partagés (mais assurez-vous d’utiliser le bon type – il en existe un différent pour les tableaux). Ils ont beaucoup à voir avec RAII .

Pour éviter les memory leaks, vous pouvez utiliser des pointeurs intelligents chaque fois que vous le pouvez. Il existe essentiellement 2 types différents de pointeurs intelligents en C ++

  • Référence comptée (ex. Boost :: shared_ptr / std :: tr1: shared_ptr)
  • non référence comptée (ex: 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 std :: containers) alors que scoped_ptr ne le peut pas. Les pointeurs comptés non référencés n’ont pratiquement pas de surcharge ni de surcharge. Le comptage des références introduit toujours une sorte de surcharge.

(Je suggère d’éviter éviter auto_ptr, il a de graves défauts si utilisé incorrectement)

Pour append un petit peu à la réponse de Sydius, les pointeurs intelligents fournissent souvent une solution plus stable en détectant de nombreuses erreurs faciles à commettre. Les pointeurs bruts auront des avantages de performance et pourront être plus flexibles dans certaines circonstances. Vous pouvez également être obligé d’utiliser des pointeurs bruts lors de la liaison avec certaines bibliothèques tierces.