web-dev-qa-db-fra.com

Différences entre unique_ptr et shared_ptr

Doublons possibles:
pimpl: shared_ptr ou unique_ptr
explication des pointeurs intelligents (boost)

Quelqu'un pourrait-il expliquer les différences entre shared_ptr et unique_ptr?

211
smallB

Ces deux classes sont des pointeurs intelligents, ce qui signifie qu'elles désallouent automatiquement (dans la plupart des cas) l'objet sur lequel elles pointent lorsque cet objet ne peut plus être référencé. La différence entre les deux est le nombre de pointeurs différents de chaque type pouvant faire référence à une ressource.

Lorsque vous utilisez _unique_ptr_, il peut y avoir au plus un _unique_ptr_ pointant sur une ressource quelconque. Lorsque ce _unique_ptr_ est détruit, la ressource est automatiquement récupérée. Etant donné qu'il ne peut y avoir qu'un seul _unique_ptr_ par ressource, toute tentative de copie d'un _unique_ptr_ entraînera une erreur lors de la compilation. Par exemple, ce code est illégal:

_unique_ptr<T> myPtr(new T);       // Okay
unique_ptr<T> myOtherPtr = myPtr; // Error: Can't copy unique_ptr
_

Cependant, _unique_ptr_ peut être déplacé à l'aide de la nouvelle sémantique de déplacement:

_unique_ptr<T> myPtr(new T);                  // Okay
unique_ptr<T> myOtherPtr = std::move(myPtr); // Okay, resource now stored in myOtherPtr
_

De même, vous pouvez faire quelque chose comme ceci:

_unique_ptr<T> MyFunction() {
    unique_ptr<T> myPtr(/* ... */);

    /* ... */

    return myPtr;
}
_

Cet idiome signifie "Je vous renvoie une ressource gérée. Si vous ne capturez pas explicitement la valeur de retour, la ressource sera nettoyée. Si vous le faites, vous êtes maintenant le propriétaire exclusif de cette ressource." De cette façon, vous pouvez considérer _unique_ptr_ comme un remplacement plus sûr et meilleur pour _auto_ptr_.

_shared_ptr_ permet en revanche à plusieurs pointeurs de pointer sur une ressource donnée. Lorsque le dernier _shared_ptr_ d'une ressource est détruit, la ressource est désallouée. Par exemple, ce code est parfaitement légal:

_shared_ptr<T> myPtr(new T);       // Okay
shared_ptr<T> myOtherPtr = myPtr; // Sure!  Now have two pointers to the resource.
_

En interne, _shared_ptr_ utilise comptage de références pour suivre le nombre de pointeurs référant à une ressource. Vous devez donc veiller à ne pas introduire de cycles de référence.

En bref:

  1. Utilisez _unique_ptr_ lorsque vous souhaitez un seul pointeur sur un objet qui sera récupéré lorsque ce pointeur unique sera détruit.
  2. Utilisez _shared_ptr_ lorsque vous souhaitez que plusieurs pointeurs pointent vers la même ressource.

J'espère que cela t'aides!

451
templatetypedef

unique_ptr est le pointeur intelligent le plus léger de votre choix si vous avez juste un objet dynamique quelque part pour lequel un consommateur a le seul (donc "unique"). ") responsabilité - peut-être une classe wrapper qui doit maintenir un objet alloué dynamiquement. unique_ptr a très peu de frais généraux. Ce n'est pas copiable, mais amovible. Son type est template <typename D, typename Deleter> class unique_ptr;, il dépend donc de deux paramètres de modèle .

unique_ptr est aussi ce que auto_ptr voulait être dans l'ancien C++ mais ne le pouvait pas en raison des limitations de ce langage.

shared_ptr est en revanche un animal très différent. La différence évidente est que de nombreux consommateurs peuvent partager la responsabilité d'un objet dynamique (donc "partagé"), et l'objet ne sera détruit que lorsque tous les pointeurs partagés auront disparu. De plus, vous pouvez avoir des pointeurs faibles qui seront informés de manière intelligente si le pointeur partagé qu'ils suivent a disparu.

En interne, shared_ptr a encore beaucoup à faire: Il existe un compte de références mis à jour de manière atomique pour permettre son utilisation dans du code concurrent. En outre, de nombreuses affectations sont en cours, une pour un "bloc de contrôle de référence" de comptabilité interne et une autre (souvent) pour l'objet membre réel.

Mais il y a une autre grande différence: le type de pointeurs partagés est toujours template <typename T> class shared_ptr;, et ceci malgré le fait que vous pouvez l'initialiser avec des deleters personnalisés et avec des allocateurs personnalisés. Le deleter et l'allocateur sont suivis à l'aide de l'effacement de type et de la répartition de la fonction virtuelle, ce qui ajoute au poids interne de la classe, mais présente l'énorme avantage que différentes sortes de pointeurs partagés de type T sont tous compatibles, quelle que soit la suppression. et les détails de l'allocation. Ainsi, ils expriment réellement le concept de "responsabilité partagée pour T" sans alourdir le consommateur avec les détails!

shared_ptr et unique_ptr sont tous deux conçus pour être passés par valeur (avec l'exigence évidente de mobilité pour le pointeur unique). Vous ne devriez pas non plus vous inquiéter pour les frais généraux, car leur puissance est vraiment stupéfiante, mais si vous avez le choix, préférez unique_ptr et utilisez uniquement shared_ptr si vous avez vraiment besoin de responsabilité partagée.

70
Kerrek SB

nique_ptr
est un pointeur intelligent qui possède un objet exclusivement.

shared_ptr
est un pointeur intelligent pour la propriété partagée. C'est à la fois copyable et movable. Plusieurs instances de pointeur intelligent peuvent posséder la même ressource. Dès que le dernier pointeur intelligent propriétaire de la ressource sera hors de portée, la ressource sera libérée.

17
Alok Save

Lorsque vous placez un pointeur dans un unique_ptr, vous ne pouvez pas avoir plusieurs copies de unique_ptr. Le shared_ptr contient un compteur de référence qui compte le nombre de copies du pointeur stocké. Chaque fois qu'un shared_ptr est copié, ce compteur est incrémenté. Chaque fois qu'un shared_ptr est détruit, ce compteur est décrémenté. Lorsque ce compteur atteint 0, l'objet stocké est détruit.

8
neodelphi