Подтвердить что ты не робот

Как объявить std:: unique_ptr и как его использовать?

Я пытаюсь понять, как работает 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);

мы использовали указатель в качестве аргумента. Разве это имеет значение?

То, что также не ясно для меня, заключается в том, как указатели, объявленные таким образом, будут отличаться от указателей, объявленных "обычным" способом.

4b9b3361

Ответ 1

Конструктор unique_ptr<T> принимает необработанный указатель на объект типа T (поэтому он принимает T*).

В первом примере:

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

Указатель является результатом выражения new, а во втором примере:

unique_ptr<double> uptr2 (pd);

Указатель хранится в переменной pd.

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

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

Таким образом, имеется два уникальных указателя, которые эффективно инкапсулируют один и тот же объект (тем самым нарушая семантику уникального указателя).

Вот почему первая форма для создания уникального указателя лучше, когда это возможно. Обратите внимание, что в С++ 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 не копируется.

До тех пор, пока вы используете интеллектуальные указатели таким образом, чтобы не нарушать неявный контракт, который они требуют от вас, вы получите гарантию, что никакая память не будет пропущена, а правильная политика владения для вашего объекта будет принудительно, Необработанные указатели не дают вам этой гарантии.

Ответ 2

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

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

похож на

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

Здесь unique_ptr автоматически удаляет пространство, занимаемое uptr.


как указатели, объявленные таким образом, будут отличаться от указателей, объявленных "обычным" способом.

Если вы создадите целое число в пространстве кучи (используя новое ключевое слово или malloc), вам придется очистить эту память самостоятельно (используя delete или free соответственно).

В приведенном ниже коде

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

Здесь вам придется удалить heapInt, когда это будет сделано с помощью. Если он не удален, происходит утечка памяти.


Для того, чтобы избежать таких утечек памяти unique_ptr используется, где unique_ptr автоматически удаляет пространство, занимаемое heapInt, когда он выходит из области видимости.

Ответ 3

Уникальные указатели гарантированно уничтожают объект, которым они управляют, когда они выходят из области видимости. http://en.cppreference.com/w/cpp/memory/unique_ptr

В этом случае:

unique_ptr<double> uptr2 (pd);

pd будет уничтожен, когда uptr2 выйдет из области видимости. Это облегчает управление памятью за счет автоматического удаления.

Случай unique_ptr<int> uptr (new int(3)); не отличается, за исключением того, что необработанный указатель здесь не назначен ни одной переменной.