多重继承和unique_ptr销毁

Dim*_*los 6 c++ inheritance vector unique-ptr c++11

我有经典(可能有问题的)多重继承钻石计划.

  • B继承A
  • C继承A
  • D继承C和B.

我想有一个std::vector可以包含两种CD 对象,所以我做它作为std::vector<C>D的爸爸和正常工作.

但是,当我使用:std::vector<std::unique_ptr<C>>然后我在矢量的破坏时有分段错误.

** glibc detected *** ./a.out: free(): invalid pointer: 0x0000000009948018***
Run Code Online (Sandbox Code Playgroud)

为什么会有区别?对我来说,即使是第一次实施也是有问题的.

#include <string>
#include <vector>
#include <memory>

class A
{
public:
    A() = default;
};

class B : public virtual A
{
public:
    B() = default;
};

class C : public virtual A
{
public:
    C() = default;
};

class D : public B, public C
{
public:
    D() = default;
};


int main()
{
    { // this crashes
    std::vector<std::unique_ptr<C>> v;
    std::unique_ptr<D> s1(new D());
    v.push_back(std::move(s1));
    std::unique_ptr<C> s2(new C());
    v.push_back(std::move(s2));
    }

    { // this is fine
    std::vector<C> v;
    D s1;
    v.push_back(s1);
    C s2;
    v.push_back(s2);
    }

    return 0;
};
Run Code Online (Sandbox Code Playgroud)

dkg*_*dkg 14

您应该将析构函数声明为虚拟.否则,如果D使用指针删除类C,则将~C()调用析构函数,并且将丢失清理的基本部分.

另请注意,在第二部分(不使用unique_ptr)中,您可以进行一些对象切片.这意味着您正在将s1类型的副本创建D到类的新对象中C,因此您可能会丢失特定于其的额外信息D.

这是更正后的代码:

#include <string>
#include <vector>
#include <memory>

class A
{
public:
    A() = default;
    virtual ~A() {};
};

class B : public virtual A
{
public:
    B() = default;
    virtual ~B() {};
};

class C : public virtual A
{
public:
    C() = default;
    virtual ~C() {};
};

class D : public B, public C
{
public:
    D() = default;
    virtual ~D() {};
};


int main()
{
    { // this does not crashe anymore
        std::vector<std::unique_ptr<C>> v;
        std::unique_ptr<D> s1(new D());
        v.push_back(std::move(s1));
        std::unique_ptr<C> s2(new C());
        v.push_back(std::move(s2));
    }

    { // this is fine because you slice D into C, still that fine ?
        std::vector<C> v;
        D s1;
        v.push_back(s1);
        C s2;
        v.push_back(s2);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

也可以看看


注意

如评论中所述,如果将析构函数标记为虚拟,则每个派生类也将具有虚拟析构函数.到处写它可以使它更明确.这是一种风格问题.


Ale*_*bin 5

你的"这很好"的例子是切片,向量只包含实例C,这就是为什么它"有效"但却没有达到预期的效果.解决方案就像dkg指出的那样是使用虚拟dtors.