web-dev-qa-db-fra.com

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

J'essaie de comprendre comment std::unique_ptr fonctionne et pour cela j'ai trouvé this document. L'auteur commence par 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 me déroute, 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 comment les pointeurs déclarés de cette façon seront différents des pointeurs déclarés de manière "normale".

71
Roman

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

Dans le premier exemple:

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

Le pointeur est le résultat d'une expression new, alors que dans le deuxième exemple:

unique_ptr<double> uptr2 (pd);

Le pointeur est stocké dans la variable pd.

Conceptuellement, rien ne change (vous construisez un unique_ptr 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 effectivement le même objet (violant ainsi la sémantique d'un 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. Passons maintenant à vos doutes:

Ce qui n’est pas non plus clair pour moi, c’est comment 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 supposés modéliser la propriété des objets et se charger automatiquement de la destruction de l'objet pointé lorsque le dernier pointeur (intelligent, propriétaire) sur cet objet est hors de portée.

De cette façon, vous n'avez pas à vous souvenir de faire delete sur des objets alloués dynamiquement - le destructeur du pointeur intelligent le fera pour vous - ni à vous demander si vous ne déréférencerez pas un pointeur (en suspens) sur 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 ne doit y avoir qu’un un (propriétaire) pointeur sur l’objet pointé. - c'est pourquoi unique_ptr n'est pas copiable.

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

66
Andy Prowl

Il n'y a pas de différence 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 nique_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 segment de mémoire (utilisez un nouveau mot clé ou malloc ), vous devrez alors vider vous-même cette mémoire (avec supprimez ou libre 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, lorsque vous avez terminé. S'il n'est pas supprimé, une fuite de mémoire se produit.


Afin d'éviter de telles fuites de mémoire, nique_ptr est utilisé, où unique_ptr supprime automatiquement l'espace occupé par heapInt lorsqu'il sort de la portée.

8
fury.slay

Les pointeurs uniques ont la garantie de détruire l’objet qu’ils gèrent s’ils sortent du domaine. http://fr.cppreference.com/w/cpp/memory/unique_ptr

Dans ce cas:

unique_ptr<double> uptr2 (pd);

pd sera détruit lorsque uptr2 sort de la portée. 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.

6
fatihk