Jak zadeklarować std :: unique_ptr i do czego służy?

95

Próbuję zrozumieć, jak std::unique_ptrdziała i do tego znalazłem ten dokument. Autor zaczyna od następującego przykładu:

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

To, co jest dla mnie mylące, to ta kwestia

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

Używamy liczby całkowitej jako argumentu (w okrągłych nawiasach) i tutaj

unique_ptr<double> uptr2 (pd);

użyliśmy wskaźnika jako argumentu. Czy to ma znaczenie?

Nie jest też dla mnie jasne, jak wskaźniki deklarowane w ten sposób będą się różniły od wskaźników deklarowanych w „normalny” sposób.

rzymski
źródło
13
new int(3)zwraca wskaźnik do nowego int, tak jak pdwskaźnik do nowego double.
David Schwartz,

Odpowiedzi:

89

Konstruktor klasy unique_ptr<T>przyjmuje surowy wskaźnik do obiektu typu T(a więc akceptuje a T*).

W pierwszym przykładzie:

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

Wskaźnik jest wynikiem newwyrażenia, podczas gdy w drugim przykładzie:

unique_ptr<double> uptr2 (pd);

Wskaźnik jest przechowywany w pdzmiennej.

Koncepcyjnie nic się nie zmienia (konstruujesz a unique_ptrz surowego wskaźnika), ale drugie podejście jest potencjalnie bardziej niebezpieczne, ponieważ pozwoliłoby ci na przykład:

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

W ten sposób posiadanie dwóch unikalnych wskaźników, które skutecznie hermetyzują ten sam obiekt (naruszając w ten sposób semantykę unikalnego wskaźnika).

Dlatego pierwsza forma tworzenia unikalnego wskaźnika jest lepsza, jeśli to możliwe. Zauważ, że w C ++ 14 będziemy mogli:

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

Co jest zarówno jaśniejsze, jak i bezpieczniejsze. A jeśli chodzi o tę twoją wątpliwość:

Nie jest też dla mnie jasne, jak wskaźniki deklarowane w ten sposób będą się różniły od wskaźników deklarowanych w „normalny” sposób.

Inteligentne wskaźniki mają modelować własność obiektu i automatycznie dbać o zniszczenie wskazanego obiektu, gdy ostatni (inteligentny, posiadający) wskaźnik do tego obiektu znajdzie się poza zakresem.

W ten sposób nie musisz pamiętać wykonywania czynności deletena obiektach alokowanych dynamicznie - destruktor inteligentnego wskaźnika zrobi to za Ciebie - ani martwić się, czy nie wyłuskujesz (wiszącego) wskaźnika do obiektu, który został już zniszczony:

{
    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

Now unique_ptrjest inteligentnym wskaźnikiem, który modeluje unikalną własność, co oznacza, że ​​w dowolnym momencie w programie będzie tylko jeden wskaźnik (będący właścicielem) wskazanego obiektu - dlatego unique_ptrnie można go skopiować.

Dopóki używasz inteligentnych wskaźników w sposób, który nie narusza niejawnej umowy, której wymagają od ciebie, będziesz mieć gwarancję, że żadna pamięć nie zostanie ujawniona, a odpowiednia polityka własności dla twojego obiektu będzie egzekwowana. Surowe wskazówki nie dają takiej gwarancji.

Andy Prowl
źródło
3
Cześć, nie mogę nic o zrozumieć model object ownership, tym integer leakw kodzie lub enforcing ownership policy for object. Czy mógłbyś zasugerować tematy / zasoby, aby nauczyć się tych koncepcji?
Płomień udun
1
Nie mogę użyć unique_ptr, bez błędu: 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.mimo że mam #include <utility>i #include <memory>. Jakakolwiek rada?
Anonimowy
15

Nie ma różnicy w działaniu obu koncepcji przypisania do unique_ptr.

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

jest podobne do

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

Tutaj unique_ptr automatycznie usuwa przestrzeń zajmowaną przez uptr.


jak wskaźniki zadeklarowane w ten sposób będą się różnić od wskaźników zadeklarowanych w „normalny” sposób.

Jeśli utworzysz liczbę całkowitą w przestrzeni sterty (używając słowa kluczowego new lub malloc ), będziesz musiał samodzielnie wyczyścić tę pamięć (używając odpowiednio delete lub free ).

W poniższym kodzie

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

Tutaj będziesz musiał usunąć heapInt, gdy zostanie to wykonane za pomocą. Jeśli nie zostanie usunięty, nastąpi przeciek pamięci.

Aby uniknąć takich wycieków pamięci, używany jest unique_ptr , gdzie unique_ptr automatycznie usuwa miejsce zajmowane przez heapInt, gdy wychodzi poza zakres. Nie musisz więc usuwać ani bezpłatnie usuwać unique_ptr.

wściekłość. gra
źródło
10

Unikalne wskaźniki gwarantują, że zniszczą obiekt, którym zarządzają, gdy wyjdą poza zasięg. http://en.cppreference.com/w/cpp/memory/unique_ptr

W tym przypadku:

unique_ptr<double> uptr2 (pd);

pdzostanie zniszczony, gdy uptr2wyjdzie poza zakres. Ułatwia to zarządzanie pamięcią poprzez automatyczne usuwanie.

Przypadek unique_ptr<int> uptr (new int(3));nie jest inny, z wyjątkiem tego, że surowy wskaźnik nie jest tutaj przypisany do żadnej zmiennej.

fatihk
źródło
-1

Z cppreference jednym z std::unique_ptrkonstruktorów jest

jawne unique_ptr (wskaźnik p) noexcept;

Tak więc, aby utworzyć nowy, std::unique_ptrnależy przekazać wskaźnik do jego konstruktora.

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

Albo to jest to samo co

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

Inna jest to, że po użyciu nie musisz sprzątać. Jeśli nie używasz std::unique_ptr(inteligentnego wskaźnika), będziesz musiał go usunąć w ten sposób

delete int_ptr;

kiedy już go nie potrzebujesz lub spowoduje to wyciek pamięci.

Tevada
źródło