这个问题令人困惑,所以这里是我试图做的精简版:
#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指向的对象。我分配它的值是即将被删除的对象中的东西。然而,通过移动它,它可能不再存在了。
虽然这段代码编译并运行良好(在valgrind中),但我想知道:这样安全吗?有没有发生过令人讨厌的事情,我没有意识到?这样做有什么警告吗?
编辑:我忘了放入虚拟析构函数。假装所有三个班级都有。它存在于我的实际代码中。
答案 0 :(得分:3)
潜在问题是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
)在左侧{之前>释放对象的所有权{已设置{1}}。
删除分配给unique_ptr
最初拥有的对象实际上是流程中发生的 last 步骤。在链表示例中,被删除的对象(旧的头节点)在被销毁时已经移动了unique_ptr
。
文体说明:
在代码中使用原始指针unique_ptr
和b
令人惊讶且令人困惑;当存在指向拥有对象的原始指针时,最终拥有c
和unique_ptr
的{{1}}不是语义唯一的。 (*b
确实很有用 - 这里的奇怪之处是你在相同的范围内有多个指针指向相同的对象。)
将*c
与unique_ptr::get()
一起使用是合理的,但不是“最佳做法”。我建议随时随地使用new
。这在异常安全方面有好处,但在我看来,它在语义上也更清晰。