147 votes

Comment déclarer std::unique_ptr et quelle en est l'utilité ?

J'essaie de comprendre comment std::unique_ptr fonctionne et pour cela j'ai trouvé este 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 est que dans cette ligne

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

Nous utilisons l'entier comme argument (entre parenthèses rondes) 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 clair non plus pour moi, c'est la façon dont les pointeurs, déclarés de cette façon, seront différents des pointeurs déclarés de façon "normale".

129voto

Andy Prowl Points 62121

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

Dans le premier exemple :

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

Le pointeur est le résultat d'un new alors que dans le second exemple :

unique_ptr<double> uptr2 (pd);

Le pointeur est stocké dans le pd variable.

D'un point de vue conceptuel, rien ne change (vous construisez une unique_ptr à partir d'un pointeur brut), mais la seconde approche est potentiellement plus dangereuse, car elle vous permettrait, par exemple, de faire :

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

Ayant ainsi deux des pointeurs uniques qui encapsulent effectivement le même objet (violant ainsi la sémantique d'un pointeur de type unique pointeur).

C'est pourquoi la première forme de création d'un pointeur unique est préférable, lorsque cela est possible. Notez 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 votre doute :

Ce qui n'est pas clair non plus pour moi, c'est la façon dont les pointeurs, déclarés de cette façon, seront différents des pointeurs déclarés de façon "normale".

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

Ainsi, vous n'avez pas à vous souvenir de faire delete sur les objets alloués dynamiquement - le destructeur du pointeur intelligent s'en chargera pour vous - ni de s'inquiéter de savoir si vous ne déréférencerez pas un pointeur (flottant) vers un objet qui a déjà été détruit :

{
    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

Maintenant unique_ptr est un pointeur intelligent qui modélise la propriété unique, ce qui signifie qu'à tout moment dans votre programme il n'y aura que un (propriétaire) de l'objet pointé - c'est la raison pour laquelle unique_ptr est non copiable.

Tant que vous utilisez les 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 perdue et que la politique de propriété appropriée pour votre objet sera appliquée. Les pointeurs bruts ne vous donnent pas cette garantie.

28voto

fury.slay Points 485

Il n'y a pas de différence de fonctionnement 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 du tas (en utilisant la commande nouveau mot-clé ou malloc ), vous devrez alors vider cette mémoire par vous-même (en utilisant la fonction supprimer ou gratuit respectivement).

Dans le code ci-dessous,

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

Ici, vous devrez supprimer heapInt, lorsqu'il a fini de l'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 sort de sa portée. Ainsi, vous n'avez pas besoin de faire supprimer ou gratuit pour unique_ptr.

15voto

FatihK Points 4776

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

Dans ce cas :

unique_ptr<double> uptr2 (pd);

pd sera détruit lorsque uptr2 sort du champ d'application. Cela facilite la gestion de la mémoire par une suppression automatique.

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

3voto

Tevada Points 31

Desde Référence cpp l'un des std::unique_ptr Les constructeurs sont

explicit unique_ptr( pointeur p ) noexcept ;

Ainsi, pour créer un nouveau std::unique_ptr est de passer un pointeur à 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 suit

delete int_ptr;

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

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