C++ Proprietà univoca (std :: unique_ptr)


Esempio

C ++ 11

Un std::unique_ptr è un modello di classe che gestisce la durata di un oggetto archiviato in modo dinamico. A differenza di std::shared_ptr , l'oggetto dinamico è di proprietà di una sola istanza di std::unique_ptr in qualsiasi momento,


// Creates a dynamic int with value of 20 owned by a unique pointer
std::unique_ptr<int> ptr = std::make_unique<int>(20);

(Nota: std::unique_ptr è disponibile da C ++ 11 e std::make_unique da C ++ 14.)

Solo la variabile ptr contiene un puntatore a un int allocato dinamicamente. Quando un puntatore univoco che possiede un oggetto esce dall'ambito, l'oggetto di proprietà viene eliminato, ovvero viene chiamato il suo distruttore se l'oggetto è di tipo classe e viene rilasciata la memoria per quell'oggetto.

Per usare std::unique_ptr e std::make_unique con tipi di array, usa le loro specializzazioni di array:

// Creates a unique_ptr to an int with value 59
std::unique_ptr<int> ptr = std::make_unique<int>(59);

// Creates a unique_ptr to an array of 15 ints
std::unique_ptr<int[]> ptr = std::make_unique<int[]>(15);

È possibile accedere a std::unique_ptr proprio come un puntatore raw, perché sovraccarica quegli operatori.


È possibile trasferire la proprietà dei contenuti di un puntatore intelligente a un altro puntatore utilizzando std::move , che farà in modo che il puntatore smart originale punti a nullptr .

// 1. std::unique_ptr
std::unique_ptr<int> ptr = std::make_unique<int>();

// Change value to 1
*ptr = 1;

// 2. std::unique_ptr (by moving 'ptr' to 'ptr2', 'ptr' doesn't own the object anymore)
std::unique_ptr<int> ptr2 = std::move(ptr);

int a = *ptr2; // 'a' is 1
int b = *ptr;  // undefined behavior! 'ptr' is 'nullptr'
               // (because of the move command above)

Passando unique_ptr a funzioni come parametro:

void foo(std::unique_ptr<int> ptr)
{
    // Your code goes here
}

std::unique_ptr<int> ptr = std::make_unique<int>(59);
foo(std::move(ptr))

Restituzione di unique_ptr dalle funzioni. Questo è il metodo preferito di scrittura C ++ 11 per le funzioni di fabbrica, in quanto trasmette chiaramente la semantica della proprietà del unique_ptr : il chiamante possiede il unique_ptr risultante ed è responsabile di esso.

std::unique_ptr<int> foo()
{
    std::unique_ptr<int> ptr = std::make_unique<int>(59);
    return ptr;
}

std::unique_ptr<int> ptr = foo();

Confronta questo a:

int* foo_cpp03();

int* p = foo_cpp03(); // do I own p? do I have to delete it at some point?
                      // it's not readily apparent what the answer is.
C ++ 14

Il modello di classe make_unique viene fornito dal C ++ 14. È facile aggiungerlo manualmente al codice C ++ 11:

template<typename T, typename... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args&&... args)
{ return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }

// Use make_unique for arrays
template<typename T>
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(size_t n)
{ return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]()); }
C ++ 11

A differenza del puntatore intelligente stupido ( std::auto_ptr ), unique_ptr può anche essere istanziato con l'allocazione vettoriale ( non con std::vector ). Gli esempi precedenti riguardavano allocazioni scalari . Ad esempio, per disporre di un array intero allocato dinamicamente per 10 elementi, si specificherà int[] come tipo di modello (e non solo int ):

std::unique_ptr<int[]> arr_ptr = std::make_unique<int[]>(10);

Quale può essere semplificato con:

auto arr_ptr = std::make_unique<int[]>(10);

Ora, usi arr_ptr come se fosse un array:

arr_ptr[2] =  10; // Modify third element

Non devi preoccuparti della disallocazione. Questa versione specializzata del modello chiama appropriatamente costruttori e distruttori. L'uso della versione vettoriale di unique_ptr o di un vector stesso è una scelta personale.

Nelle versioni precedenti al C ++ 11, std::auto_ptr era disponibile. A differenza di unique_ptr , è consentito copiare auto_ptr s, in base al quale il sorgente ptr perderà la proprietà del puntatore contenuto e il target lo riceverà.