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.