2016-03-08 4 views
4

Я понимаю, что unique_ptr является единственным владельцем какого-либо объекта, и он освобождает этот объект, когда он выходит из сферы видимости. То, что я не понимаю, следующая ситуация:Что происходит при перенаправлении unique_ptr?

unique_ptr<int> p(new int(1)); 
p = unique_ptr<int>(new int(2)); 

Что происходит с первым объектом new int(1) если p перенаправляется в другое место памяти new int(2)p может владеть только один из них)?

+1

Это потеряно, ушло навсегда, но гарантировано, что оно будет очищено должным образом. Если тип не является POD, его деструктор будет вызываться (если не используется какой-либо пользовательский деаллокатор). – Tas

+0

Вот чего я не понимаю. Кто его убережет, так как он никому не принадлежит? – Tracer

+0

'unique_ptr' - это объект, как любой другой, с конструктором и деструктором. Когда вызывается 'p = unique_ptr', вызывается деструктор для' p', который очищает память. – Tas

ответ

7

unique_ptr уничтожает объект его собственности, когда unique_ptr является уничтожить ed или повторно закрыты. Например:

#include <iostream> 
#include <memory> 
using namespace std; 

struct T { 
    T(int x) : x(x) { 
     cout << "T(" << x << ")\n"; 
    } 
    ~T() { 
     cout << "~T(" << x << ")\n"; 
    } 
    int x; 
}; 

int main() { 
    unique_ptr<T> p(new T(1)); 
    p = unique_ptr<T>(new T(2)); 
} 

Это будет печатать:

  • T(1) при создании первого объекта.
  • T(2) при создании второго объекта.
  • ~T(1), когда первый объект освобождается оператором присваивания p.
  • ~T(2), когда второй объект освобождается деструктором p.
+0

Если 'T (1)' уничтожается оператором присваивания 'p', что происходит в этом случае? 'unique_ptr p (новый T (1)); unique_ptr p2 (новый T (2)); p2 = move (p); 'Кто уничтожил' новый T (2) '? – Tracer

+0

@Tracer: 'p2' сделал, когда ему было назначено. – GManNickG

+0

@Tracer: оператор присваивания 'p2' должен уничтожить' new T (2) 'перед перемещением' new T (1) 'в' p2' из 'p'. –

1

unique_ptr определен, чтобы убедиться, что первый int правильно освобожден, поэтому он вызывает на нем delete, освобождая зарезервированную память.

Это несколько идентична этому коду:

int* p = new int(1); 
delete p; 
p = new int(2); 

Что происходит в деталях заключается в следующем:

  • создается новый Integer с помощью new int(1).
  • Вы передаете указатель на этот новый int только что созданному экземпляру unique_ptr с именем p. Это объект, который просто сохраняет указатель на данный момент.
  • Вы создаете второй int, используя new int(2).
  • Вы передаете этот указатель новому unique_ptr, используя unique_ptr<int>(new int(2)), это временный экземпляр unique_ptr (мы увидим, почему в секунду), и он сохранит указатель на второй int.
  • Вы назначаете временный объект p. Теперь оператор присваивания определяется для удаления ранее принадлежащего ему объекта (первого int) и получения права собственности на объект, принадлежащий присвоенному unique_ptr (второй int). Ниже приведена реализация. На этом этапе p владеет вторым int, первый int удаляется, а у временного больше нет объекта (удерживая nullptr).
  • В качестве последней части временный unique_ptr выходит за пределы области видимости, так как мы никогда не давали ему имени или не хранили ссылку на него, поэтому его деструктор вызывается. Но он все равно содержит nullptr.

Таким образом, более подробный эквивалент с использованием сырых указателей будет что-то вроде этого:

int* p = new int(1); //create an int 
{ 
    int* tmp = new int(2); //create second int 
    int* del = p; //we need to delete this (first int) 

    //take ownership of the temporary (second int) 
    p = tmp; 
    tmp=nullptr; 

    //delete the old object (first int) 
    delete del; 
} //tmp and del go out of scope here, but tmp holds the nullptr and del is deleted 
//first int is deleted, p points to the second int here 

Edit для Tracer: Это реализация используется Visual Studio (комментарий является частью <memory>, как а):

typedef unique_ptr<_Ty> _Myt; 

_Myt& operator=(_Myt&& _Right) _NOEXCEPT 
{ // assign by moving _Right 
    if (this != &_Right) 
    { // different, do the move 
     reset(_Right.release()); 
     this->get_deleter() = _STD forward<_Dx>(_Right.get_deleter()); 
    } 
return (*this); 
} 

void reset(pointer _Ptr = pointer()) _NOEXCEPT 
{ // establish new pointer 
    pointer _Old = get(); 
    this->_Myptr() = _Ptr; 
    if (_Old != pointer()) 
     this->get_deleter()(_Old); 
} 
+0

Коммутационные указатели? Вы уверены, что? Есть ли что-нибудь в стандарте C++, поддерживающем этот оператор? – Tracer

+0

@Tracer: я смешал его с shared_ptr. При реализации shared_ptr, который поставляется с визуальной студией, временный shared_ptr копируется в оператор присваивания. Затем текущий указатель переключается с временным. Когда временное значение выходит за пределы области действия, shared_ptr сам содержит присвоенное значение, а временное значение хранит старое значение, уменьшая его количество ссылок/разрушая объект. Но поскольку этот вопрос касается unique_ptr, я соответствующим образом обновил ответ. – Anedar

Смежные вопросы