这个问题令人困惑,所以这里是我正在尝试做的事情的精简版:
#include <memory>
#include <iostream>
class A {
};
class B : public A {
public:
std::unique_ptr<A> a;
};
class C : public A {
public:
int var;
};
int main()
{
C* c = new C;
B* b = new B;
c->var = 3;
b->a = std::unique_ptr<A>(c);
std::unique_ptr<A> aa(b);
aa = std::move(static_cast<B*>(aa.get())->a);
std::cout << static_cast<C*>(aa.get())->var;
}
我是这样做的:
- 创建一个自身拥有 unique_ptr 的类(我的具体情况是多态的,所以这就是我在这里所做的)
- 为该类创建一个 unique_ptr
- 将外部 unique_ptr 赋值给内部的 unique_ptr
现在,最后一步将销毁 unique_ptr 指向的对象。我分配给它的值是那个即将被删除的对象中的东西。但是,通过移动它,它可能不再在那里了。
虽然这段代码编译和运行良好(在 valgrind 中也是如此),但我想知道:这安全吗?是不是发生了什么我没有意识到的坏事?这样做有什么注意事项吗?
编辑:我忘了放入虚拟析构函数。假装所有三个类(class)都有。它在我的实际代码中。
最佳答案
这是安全的。
根本问题是unique_ptr
的移动赋值是如何工作的。请注意,一个更简单的示例(不涉及继承)演示了与您的代码相同的问题,它是使用 unique_ptr
的单链表:
class Node {
public:
unique_ptr<Node> next;
};
// ....
list_head = make_unique<Node>();
list_head->next = make_unique<Node>();
// Delete the head:
list_head = list_head->next;
关键问题是,旧*list_head
节点的销毁(作为赋值操作的一部分发生)是否也会导致正在提升为头节点的节点销毁?
请注意,如果 unique_ptr
无法处理这种简单的情况,那将是非常令人惊讶的!
根据 CppReference,赋值运算符 behaves "as if by calling reset(r.release())
" .这意味着 r
(右侧,在本例中为最初拥有被提升节点的 unique_ptr
)在 之前释放对象的所有权左侧 unique_ptr
已设置。
删除最初由分配给 unique_ptr
拥有的对象实际上是该过程中发生的最后步骤。在链表示例中,被删除的对象(旧头节点)在被销毁时已经有一个移出的unique_ptr
。
风格注释:
在代码中使用原始指针 b
和 c
令人惊讶和困惑;当存在原始指针时,最终拥有 *b
和 *c
的 unique_ptr
在语义上不是唯一的指向拥有的对象。 (unique_ptr::get()
确实很有用——这里的奇怪之处在于您在同一范围内有多个指针指向相同的对象。)
将 new
与 unique_ptr
一起使用是合理的,但不是“最佳实践”。我建议随时随地使用 make_unique
。这在异常安全方面有好处,但在我看来,它在语义上也更清晰。
关于c++ - 将 unique_ptr 分配给第一个内的另一个 unique_ptr 是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42542331/