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

时间:2017-03-01 21:24:34

标签: 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;
}

这就是我所做的:
  - 创建一个包含unique_ptr的类(我的具体情况是多态的,这就是我在这里所做的)
  - 制作该班级的unique_ptr   - 为外部unique_ptr分配内部值

现在,最后一步将破坏unique_ptr指向的对象。我分配它的值是即将被删除的对象中的东西。然而,通过移动它,它可能不再存在了。

虽然这段代码编译并运行良好(在valgrind中),但我想知道:这样安全吗?有没有发生过令人讨厌的事情,我没有意识到?这样做有什么警告吗?

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

1 个答案:

答案 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_ptrb令人惊讶且令人困惑;当存在指向拥有对象的原始指针时,最终拥有cunique_ptr的{​​{1}}不是语义唯一的。 (*b确实很有用 - 这里的奇怪之处是你在相同的范围内有多个指针指向相同的对象。)

*cunique_ptr::get()一起使用是合理的,但不是“最佳做法”。我建议随时随地使用new。这在异常安全方面有好处,但在我看来,它在语义上也更清晰。