为啥 std::unique_ptr 重置与赋值不同?

Posted

技术标签:

【中文标题】为啥 std::unique_ptr 重置与赋值不同?【英文标题】:Why is std::unique_ptr reset not the same as assignment?为什么 std::unique_ptr 重置与赋值不同? 【发布时间】:2018-10-13 06:07:54 【问题描述】:

我试图理解为什么

std::unique_ptr<MyClass> p = new MyClass; 

不起作用,但是

std::unique_ptr<MyClass> p;
p.reset(new MyClass);

没问题。我有点理解它们的不同之处,但我想知道为什么选择让它们不同。赋值不等于reset有什么危险?

【问题讨论】:

std::unique_ptr&lt;MyClass&gt;MyClass 的类型不同。 【参考方案1】:

首先,std::unique_ptr&lt;MyClass&gt; p = new MyClass; 不是赋值,而是copy initialization。而且它不起作用,因为采用原始指针的constructor of std::unique 被标记为explicit

explicit unique_ptr( pointer p ) noexcept;

它被声明为explicit 以避免意外(可能是危险的)隐式转换,例如:

void foo(std::unique_ptr<int> uptr);

int *rptr = new int;
foo(rptr); // suppose rptr is implicitly converted to std::unique_ptr<int>
           // then the ownership is passed to the parameter uptr

// when foo() returns uptr is destroyed; the pointer managed by it is deleted too
// since rptr has been deleted continue to deference on it leads to UB
*rptr = 42; // UB

注意explicit 构造函数在copy initialization 中不被考虑(例如std::unique_ptr&lt;MyClass&gt; p = new MyClass;)。您可以在direct initialization 中使用它们(例如std::unique_ptr&lt;MyClass&gt; p (new MyClass);)。它们用于禁止隐式转换,但您可以执行显式转换。就像reset 的用法一样,您必须明确地 做这些事情,以表明(并让自己)您对自己在做什么非常确定。

顺便说一句:原始指针的赋值也不起作用,因为std::unique_ptr 没有将原始指针作为参数的重载赋值运算符。由于上述原因,原始指针不能隐式转换为std::unique_ptr,因此也不会考虑移动赋值运算符(以std::unique_ptr为参数)。

【讨论】:

但是std::unique_ptr&lt;MyClass&gt; p; p.reset (new MyClass); p = new MyClass; 确定是分配吗?仍然不允许。我可以看到函数调用,隐式构造函数是如何非常危险的。 @starmole:因为它可能意外发生。与意外键入“.reset()”相比,您更可能不小心将裸指针分配给 unique_ptr 请注意,如果您确实需要重新分配指针,您可以简单地执行p = std::unique_ptr&lt;MyClass&gt;(new MyClass);,或者,如果您已经使用 C++14,则更好,p = std::make_unique&lt;MyClass&gt;()。跨度> @starmole 是的,如果有 std::unique_ptr&lt;T&gt;::operator = (T *) 会被调用。但请注意,这不仅有风险,而且毫无用处:无论如何,您都不应该让原始的拥有指针四处飘荡。 创建唯一指针的惯用方式是auto p = std::make_unique&lt;MyClass&gt;()。在答案中这样说会很有用。【参考方案2】:

我试图理解为什么 std::unique_ptr&lt;MyClass&gt; p = new MyClass; 不工作

与@songyuanyao 提到的相同原因,它被声明为explicit,告诉您仍然可以在超过explicit 的a different form of initialization 中初始化它:

// Valid, since now it's 'explicit'
std::unique_ptr<MyClass> p  new MyClass ;

【讨论】:

以上是关于为啥 std::unique_ptr 重置与赋值不同?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能在 C++14 的 lambda 中移动 std::unique_ptr?

为啥 std::unique_lock 改变 std::unique_ptr?

使用带有 unique_ptr 向量的赋值运算符

将 const std::unique_ptr 用于 pimpl 习惯用法

智能指针unique_ptr用法

[C++11]独占的智能指针unique_ptr的初始化和使用