Come dichiarare std :: unique_ptr e a cosa serve?


95

Cerco di capire come std::unique_ptrfunziona e per questo ho trovato questo documento. L'autore parte dal seguente esempio:

#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();

Ciò che mi confonde è che in questa linea

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

Usiamo integer come argomento (tra parentesi tonde) e qui

unique_ptr<double> uptr2 (pd);

abbiamo usato un puntatore come argomento. Fa qualche differenza?

Ciò che inoltre non mi è chiaro, è come i puntatori, dichiarati in questo modo, saranno diversi dai puntatori dichiarati in modo "normale".


13
new int(3)restituisce un puntatore al nuovo int, proprio come lo pdera un puntatore al nuovo double.
— David Schwartz

Risposte:


89

Il costruttore di unique_ptr<T>accetta un puntatore grezzo a un oggetto di tipo T(quindi accetta a T*).

Nel primo esempio:

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

Il puntatore è il risultato di newun'espressione, mentre nel secondo esempio:

unique_ptr<double> uptr2 (pd);

Il puntatore è memorizzato nella pdvariabile.

Concettualmente, non cambia nulla (stai costruendo un unique_ptrda un puntatore grezzo), ma il secondo approccio è potenzialmente più pericoloso, poiché ti consentirebbe, ad esempio, di fare:

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

Avendo così due puntatori univoci che incapsulano efficacemente lo stesso oggetto (violando così la semantica di un puntatore univoco ).

Questo è il motivo per cui il primo modulo per creare un puntatore univoco è migliore, quando possibile. Notare che in C ++ 14 saremo in grado di fare:

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

Che è sia più chiaro che più sicuro. Ora riguardo a questo tuo dubbio:

Ciò che inoltre non mi è chiaro, è come i puntatori, dichiarati in questo modo, saranno diversi dai puntatori dichiarati in modo "normale".

I puntatori intelligenti dovrebbero modellare la proprietà dell'oggetto e si occupano automaticamente di distruggere l'oggetto appuntito quando l'ultimo puntatore (intelligente, proprietario) a quell'oggetto esce dal campo di applicazione.

In questo modo non devi ricordare di aver fatto deletesu oggetti allocati dinamicamente - il distruttore del puntatore intelligente lo farà per te - né preoccuparti se non dereferenzerai un puntatore (penzolante) a un oggetto che è già stato distrutto:

{
    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

Ora unique_ptrè un puntatore intelligente che modella la proprietà unica, il che significa che in qualsiasi momento nel tuo programma ci sarà solo un puntatore (proprietario) all'oggetto appuntito - ecco perché unique_ptrnon è copiabili.

Finché utilizzi i puntatori intelligenti in un modo che non infrange il contratto implicito che richiedono di rispettare, avrai la garanzia che non verrà persa alcuna memoria e verrà applicata la politica di proprietà appropriata per il tuo oggetto. I puntatori grezzi non ti danno questa garanzia.


3
Ciao, non sono riuscito a capire nulla su model object ownership, integer leaknel codice o enforcing ownership policy for object. Potresti suggerire argomenti / risorse per apprendere questi concetti?
— Fiamma di udun

1
Non posso usare unique_ptr, senza ottenere un errore:, 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.anche se ho #include <utility>e #include <memory>. Qualche consiglio?
— Anonimo

15

Non c'è differenza nel lavorare in entrambi i concetti di assegnazione a unique_ptr.

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

è simile a

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

Qui unique_ptr cancella automaticamente lo spazio occupato da uptr.


come i puntatori, dichiarati in questo modo, saranno diversi dai puntatori dichiarati in modo "normale".

Se crei un numero intero nello spazio dell'heap (usando new keyword o malloc ), dovrai cancellare quella memoria da solo (usando rispettivamente delete o free ).

Nel codice sottostante,

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

Qui, dovrai eliminare heapInt, quando avrai finito di usare. Se non viene eliminato, si verifica una perdita di memoria.

Per evitare tali perdite di memoria viene utilizzato unique_ptr , dove unique_ptr cancella automaticamente lo spazio occupato da heapInt quando esce dall'ambito. Quindi, non è necessario eliminare o liberare per unique_ptr.


10

È garantito che i puntatori univoci distruggano l'oggetto che gestiscono quando escono dall'ambito. http://en.cppreference.com/w/cpp/memory/unique_ptr

In questo caso:

unique_ptr<double> uptr2 (pd);

pdverrà distrutto quando uptr2esce dall'ambito. Ciò facilita la gestione della memoria tramite la cancellazione automatica.

Il caso di unique_ptr<int> uptr (new int(3));non è diverso, tranne per il fatto che il puntatore grezzo non è assegnato a nessuna variabile qui.


-1

Da cppreference , uno dei std::unique_ptrcostruttori è

esplicito unique_ptr (puntatore p) noexcept;

Quindi crearne uno nuovo std::unique_ptrsignifica passare un puntatore al suo costruttore.

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

Oppure è lo stesso di

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

La differenza è che non devi pulire dopo averlo usato. Se non usi std::unique_ptr(puntatore intelligente), dovrai eliminarlo in questo modo

delete int_ptr;

quando non ne hai più bisogno o causerà una perdita di memoria.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.