重定向 unique_ptr 时会发生什么?

What happens when redirecting unique_ptr?

我知道 unique_ptr 是某个对象的单一所有者,当它超出范围时它会释放该对象。我不明白的是以下情况:

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

如果 p 被重定向到另一个内存位置 new int(2)(因为 p 只能拥有其中一个),第一个对象 new int(1) 会发生什么情况?

unique_ptr 被定义为确保第一个 int 被正确释放,因此它在其上调用 delete,释放保留的内存。

它与此代码有些相同:

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

详细情况是这样的:

  • 您使用 new int(1) 创建了一个新的整数。
  • 您将指向此新 int 的指针传递给刚刚创建的名为 punique_ptr 实例。这是一个暂时只存储指针的对象。
  • 您使用 new int(2) 创建第二个 int。
  • 您使用 unique_ptr<int>(new int(2)) 将此指针传递给新的 unique_ptr,这是 unique_ptr 的临时实例(我们稍后会看到原因)并将指针存储到第二个整数。
  • 您将临时对象分配给 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

为 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);
 }

unique_ptrunique_ptr 被销毁 或重新安装 时销毁它拥有的对象。例如:

#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.
  • 的析构函数释放时