将 unique_ptr 分配给第一个内的另一个 unique_ptr 是否安全?

sud*_*dgy 1 c++ unique-ptr

这个问题令人困惑,所以这是我正在尝试做的精简版本:

#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;
}
Run Code Online (Sandbox Code Playgroud)

这就是我所做的:
- 创建一个类,它自己拥有一个 unique_ptr(我的具体情况是多态的,所以这就是我在这里所做的)
- 为该类创建一个 unique_ptr
- 将外部 unique_ptr 分配给内部的值

现在,最后一步将销毁 unique_ptr 指向的对象。我分配给它的值在那个即将被删除的对象中。但是,通过移动它,它可能不再在那里了。

虽然这段代码编译并运行良好(在 valgrind 中也是如此),但我想知道:这安全吗?是否发生了一些我没有意识到的令人讨厌的事情?这样做有什么注意事项吗?

编辑:我忘了放入虚拟析构函数。假设所有三个类都有。它在我的实际代码中。

Kyl*_*and 5

这是安全的。

根本问题是移动分配如何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;
Run Code Online (Sandbox Code Playgroud)

关键问题是,旧*list_head节点的破坏(作为赋值操作的一部分发生)是否也会导致被提升为头节点的节点的破坏?

请注意,如果unique_ptr无法处理这种简单的情况,那将是非常令人惊讶的!

根据 CppReference,赋值运算符的行为“就像通过调用一样reset(r.release())。这意味着r(右侧,在这种情况下unique_ptr,最初拥有被提升的节点)在左侧设置之前释放对象的所有权unique_ptr

删除原本属于被赋值对象的对象,unique_ptr实际上是过程中发生的最后一步。在链表示例中,被删除的对象(旧的头节点)unique_ptr在它被销毁时已经有一个移动的对象。


文体注释:

具有原始指针b,并c在你的代码是令人惊讶和困惑; 当原始指针指向所拥有的对象时unique_ptr,最终拥有*b并且*c语义上不是唯一的s 。(unique_ptr::get()确实很有用——这里的奇怪之处在于,您在同一范围内有多个指向同一对象的指针。)

使用newwithunique_ptr是合理的,但不是“最佳实践”。我建议make_unique随时随地使用。这在异常安全方面有好处,但在我看来,它在语义上也更清晰。