Comment déclarer std :: unique_ptr et à quoi sert-il?

94

J'essaie de comprendre comment std::unique_ptrfonctionne et pour cela j'ai trouvé ce document. L'auteur part de l'exemple suivant:

#include <utility>  //declarations of unique_ptr
using std::unique_ptr;
// default construction
unique_ptr<int> up; //creates an empty object
// initialize with an argument
unique_ptr<int> uptr (new int(3));
double *pd= new double;
unique_ptr<double> uptr2 (pd);
// overloaded * and ->
*uptr2 = 23.5;
unique_ptr<std::string> ups (new std::string("hello"));
int len=ups->size();

Ce qui est déroutant pour moi, c'est que dans cette ligne

unique_ptr<int> uptr (new int(3));

Nous utilisons un entier comme argument (entre parenthèses) et ici

unique_ptr<double> uptr2 (pd);

nous avons utilisé un pointeur comme argument. Cela fait-il une différence?

Ce qui n'est pas non plus clair pour moi, c'est en quoi les pointeurs, déclarés de cette manière, seront différents des pointeurs déclarés de manière "normale".

romain
la source
13
new int(3)renvoie un pointeur vers le nouveau int, tout comme l' pdétait un pointeur vers le nouveau double.
David Schwartz

Réponses:

87

Le constructeur de unique_ptr<T>accepte un pointeur brut vers un objet de type T(donc, il accepte a T*).

Dans le premier exemple:

unique_ptr<int> uptr (new int(3));

Le pointeur est le résultat d'une newexpression, tandis que dans le deuxième exemple:

unique_ptr<double> uptr2 (pd);

Le pointeur est stocké dans la pdvariable.

Conceptuellement, rien ne change (vous construisez un à unique_ptrpartir d'un pointeur brut), mais la deuxième approche est potentiellement plus dangereuse, car elle vous permettrait, par exemple, de faire:

unique_ptr<double> uptr2 (pd);
// ...
unique_ptr<double> uptr3 (pd);

Ainsi avoir deux pointeurs uniques qui encapsulent efficacement le même objet (violant ainsi la sémantique d'un pointeur unique ).

C'est pourquoi le premier formulaire de création d'un pointeur unique est préférable, lorsque cela est possible. Remarquez qu'en C ++ 14, nous pourrons faire:

unique_ptr<int> p = make_unique<int>(42);

Ce qui est à la fois plus clair et plus sûr. Maintenant concernant ce doute qui est le vôtre:

Ce qui n'est pas non plus clair pour moi, c'est en quoi les pointeurs, déclarés de cette manière, seront différents des pointeurs déclarés de manière "normale".

Les pointeurs intelligents sont censés modéliser la propriété de l'objet et se charger automatiquement de détruire l'objet pointé lorsque le dernier pointeur (intelligent, propriétaire) vers cet objet tombe hors de portée.

De cette façon , vous n'avez pas à vous rappeler de faire deletesur les objets attribués dynamiquement - le destructor du pointeur intelligent fera pour vous - ni à se soucier de savoir si vous ne serez pas déréférencer un (ballants) pointeur vers un objet qui a été détruit déjà:

{
    unique_ptr<int> p = make_unique<int>(42);
    // Going out of scope...
}
// I did not leak my integer here! The destructor of unique_ptr called delete

Il unique_ptrs'agit maintenant d' un pointeur intelligent qui modélise la propriété unique, ce qui signifie qu'à tout moment dans votre programme, il ne doit y avoir qu'un seul pointeur (propriétaire) vers l'objet pointé - c'est pourquoi il unique_ptrn'est pas copiable.

Tant que vous utilisez des pointeurs intelligents d'une manière qui ne rompt pas le contrat implicite qu'ils vous demandent de respecter, vous aurez la garantie qu'aucune mémoire ne sera divulguée et que la politique de propriété appropriée pour votre objet sera appliquée. Les pointeurs bruts ne vous donnent pas cette garantie.

Andy Prowl
la source
3
Salut, je ne comprenais rien model object ownership, le integer leakdans le code ou enforcing ownership policy for object. Pourriez-vous s'il vous plaît suggérer des sujets / ressources pour apprendre ces concepts?
Flame of udun
1
Je ne peux pas utiliser unique_ptr, sans obtenir une erreur:, The text ">" is unexpected. It may be that this token was intended as a template argument list terminator but the name is not known to be a template.même si j'ai #include <utility>et #include <memory>. Aucun conseil?
Anonyme
15

Il n'y a aucune différence de travail dans les deux concepts d'affectation à unique_ptr.

int* intPtr = new int(3);
unique_ptr<int> uptr (intPtr);

est similaire à

unique_ptr<int> uptr (new int(3));

Ici, unique_ptr supprime automatiquement l'espace occupé par uptr.


comment les pointeurs, déclarés de cette manière, seront différents des pointeurs déclarés de manière "normale".

Si vous créez un entier dans l'espace de tas (en utilisant un nouveau mot-clé ou malloc ), vous devrez effacer cette mémoire vous-même (en utilisant respectivement delete ou free ).

Dans le code ci-dessous,

int* heapInt = new int(5);//initialize int in heap memory
.
.//use heapInt
.
delete heapInt;

Ici, vous devrez supprimer heapInt, lorsque vous aurez fini d'utiliser. S'il n'est pas supprimé, une fuite de mémoire se produit.

Afin d'éviter de telles fuites de mémoire, unique_ptr est utilisé, où unique_ptr supprime automatiquement l'espace occupé par heapInt lorsqu'il est hors de portée. Donc, vous n'avez pas besoin de supprimer ou de libérer pour unique_ptr.

fureur.slay
la source
10

Les pointeurs uniques sont garantis pour détruire l'objet qu'ils gèrent lorsqu'ils sont hors de portée. http://en.cppreference.com/w/cpp/memory/unique_ptr

Dans ce cas:

unique_ptr<double> uptr2 (pd);

pdsera détruit lorsqu'il uptr2sort du champ d'application. Cela facilite la gestion de la mémoire par suppression automatique.

Le cas de unique_ptr<int> uptr (new int(3));n'est pas différent, sauf que le pointeur brut n'est affecté à aucune variable ici.

fatihk
la source
-1

De cppreference , l'un des std::unique_ptrconstructeurs est

explicite unique_ptr (pointeur p) noexcept;

Donc, créer un nouveau, std::unique_ptrc'est passer un pointeur vers son constructeur.

unique_ptr<int> uptr (new int(3));

Ou c'est la même chose que

int *int_ptr = new int(3);
std::unique_ptr<int> uptr (int_ptr);

La différence est que vous n'avez pas à nettoyer après l'avoir utilisé. Si vous n'utilisez pas std::unique_ptr(pointeur intelligent), vous devrez le supprimer comme ceci

delete int_ptr;

lorsque vous n'en avez plus besoin ou cela provoquera une fuite de mémoire.

Tevada
la source