Як оголосити std :: unique_ptr і яка від нього користь?


95

Я намагаюся зрозуміти, як це std::unique_ptrпрацює, і для цього знайшов цей документ. Автор починає з наступного прикладу:

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

Мене бентежить те, що в цьому рядку

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

Ми використовуємо ціле число як аргумент (між круглими дужками) і тут

unique_ptr<double> uptr2 (pd);

ми використали покажчик як аргумент. Чи має це якесь значення?

Мені також незрозуміло, так це те, як покажчики, оголошені таким чином, будуть відрізнятися від покажчиків, оголошених "нормальним" способом.


13
new int(3)повертає покажчик на нове int, як pdі вказівник на нове double.
Девід Шварц

Відповіді:


89

Конструктор of unique_ptr<T>приймає сирий вказівник на об'єкт типу T(отже, він приймає a T*).

У першому прикладі:

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

Покажчик є результатом newвиразу, тоді як у другому прикладі:

unique_ptr<double> uptr2 (pd);

Покажчик зберігається у pdзмінній.

Концептуально нічого не змінюється (ви будуєте a unique_ptrз необробленого вказівника), але другий підхід є потенційно більш небезпечним, оскільки це дозволить вам, наприклад, зробити:

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

Таким чином, маючи два унікальних вказівники, які ефективно інкапсулюють один і той же об’єкт (тим самим порушуючи семантику унікального вказівника).

Ось чому перша форма для створення унікального вказівника є кращою, коли це можливо. Зверніть увагу, що в C ++ 14 ми зможемо зробити:

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

Що одночасно і зрозуміліше, і безпечніше. А щодо цього вашого сумніву:

Мені також незрозуміло, так це те, як покажчики, оголошені таким чином, будуть відрізнятися від покажчиків, оголошених "нормальним" способом.

Розумні вказівники повинні моделювати право власності на об'єкт і автоматично дбати про знищення загостреного об'єкта, коли останній (розумний, володіючий) вказівник на цей об'єкт виходить за межі області дії.

Таким чином, вам не потрібно пам’ятати, як це робили delete об’єкти, які розподіляються динамічно - деструктор розумного вказівника зробить це за вас, - і не турбуватися про те, чи не буде ви розрингувати (звисаючий) вказівник на вже зруйнований об’єкт:

{
    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

Зараз unique_ptrє розумний вказівник, який моделює унікальне право власності, а це означає, що будь-коли у вашій програмі повинен бути лише один (власник) вказівник на вказаний об’єкт - ось чому unique_ptrвін не підлягає копіюванню.

Поки ви використовуєте розумні вказівники таким чином, щоб не порушувати неявного договору, який вони вимагають від вас дотримуватися, ви будете мати гарантію, що пам'ять не буде витікати, а відповідна політика власності на ваш об'єкт буде забезпечена. Сирі вказівники не дають вам цієї гарантії.


3
Привіт, я нічого не міг зрозуміти model object ownership, integer leakв коді або enforcing ownership policy for object. Не могли б ви запропонувати теми / ресурси для вивчення цих понять?
Полум'я удуна

1
Я не можу використовувати unique_ptr, не отримуючи помилку:, 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.навіть якщо у мене є #include <utility>і #include <memory>. Будь-яка порада?
Анонім

15

Немає різниці в роботі в обох концепціях присвоєння unique_ptr.

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

є подібним до

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

Тут unique_ptr автоматично видаляє простір, який займає uptr.


наскільки покажчики, оголошені таким чином, будуть відрізнятися від покажчиків, оголошених "нормальним" способом.

Якщо ви створюєте ціле число у просторі купи (використовуючи нове ключове слово або malloc ), то вам доведеться самостійно очистити цю пам'ять (за допомогою видалення або звільнення відповідно).

У наведеному нижче коді

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

Тут вам доведеться видалити heapInt, коли це буде зроблено за допомогою. Якщо його не видалити, то відбувається витік пам'яті.

Щоб уникнути таких витоків пам'яті , використовується унікальний_ptr , де "унікальний_птр" автоматично видаляє простір, який займає heapInt, коли він виходить за межі обсягу. Отже, вам не потрібно видаляти або безкоштовно видавати унікальний_птр.


10

Унікальні вказівники гарантовано знищують об’єкт, яким вони керують, коли вони виходять за межі зони дії. http://en.cppreference.com/w/cpp/memory/unique_ptr

В цьому випадку:

unique_ptr<double> uptr2 (pd);

pd буде знищений, коли uptr2 вийде за межі сфери дії. Це полегшує управління пам'яттю шляхом автоматичного видалення.

Випадок unique_ptr<int> uptr (new int(3));не відрізняється, за винятком того, що вихідний покажчик тут не присвоєний жодній змінній.


-1

З cppreference , одним з std::unique_ptrконструкторів є

явний unique_ptr (покажчик p) noexcept;

Отже, створити новий std::unique_ptr- це передати вказівник на його конструктор.

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

Або це те саме, що

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

Різне в тому, що вам не доведеться прибирати після його використання. Якщо ви не використовуєте std::unique_ptr(розумний вказівник), вам доведеться видалити його таким чином

delete int_ptr;

коли він вам більше не потрібен, або це призведе до витоку пам'яті.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.