当我在c++11中看到std::unique_ptr
时,我就很喜欢它,但我很长一段时间都在质疑它的有效性。(有关实时代码的链接,请参阅下面的链接(:
#include <memory>
std::unique_ptr<int> get();
extern std::unique_ptr<int> val;
void foo()
{
val = get();
}
这给了我16条关于-O3
最后叮当声的指示。但更有趣的是,它生成了两个对delete
的调用,尽管第二个永远不会被调用。
我试着这样做:
void foo()
{
auto ptr = get().release();
val.reset(ptr);
}
突然间,只有11条指令。然后我更深入地攻击了unique_ptr move ctor。最初它被实现为reset(__u.release());
。我基本上只是重新排序如下:
auto& ptr = _M_ptr();
if (ptr)
_M_deleter()(ptr);
ptr = __u.release();
Aand。。。。手动管理版本中的11条指令。这略有不同,但似乎还可以。
我把实验保存在这里。
有人能指出是我错过了什么吗?还是这实际上是有意为之?
移动分配的操作顺序必须为:
- 制作托管源指针的副本,并在源对象中将其置空
- 如果目标托管指针不为null,请将其删除
- 在目标对象中设置目标托管指针
请注意,这种逻辑很好地导致了自移动分配的无操作。
逻辑必须是这样的原因是源可能由目的地所有。
想象一下:
struct list { std::unique_ptr<list> next; };
std::unique_ptr<list> head;
// ...
if (head) head = std::move(head->next);
如果head->next
的托管指针在删除由head
管理的旧对象之前没有为null,则这将不会正常工作。
因此,在代码中,移动分配只是:
reset(source.release())
您的最后一个代码片段显然无法在删除前使源为null,因此对于移动分配来说不是一个可行的实现:
auto& ptr = _M_ptr();
if (ptr)
_M_deleter()(ptr);
ptr = __u.release();
这就留下了为什么的问题
val = get();
与不同
auto ptr = get().release();
val.reset(ptr);
区别在于从get()
返回的隐式unique_ptr<int>
。在第一个版本中,它在release
和reset
之后被销毁。在第二个版本中,它在release
之后但在reset
之前被销毁。在这两种情况下,它在被销毁时都为null,不需要进行删除。但是编译器一定无法在reset
中传播此指针为空的知识。