多重继承和unique_ptr销毁

Multiple inheritance and unique_ptr destruction

本文关键字:ptr 销毁 unique 多重继承      更新时间:2023-10-16

我有一个经典的(可能有问题的)多重继承菱形方案。

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

我想要一个可以包含CDstd::vector对象,所以我把它作为std::vector<C>,这是D的父亲和它没问题。

BUT当我使用:std::vector<std::unique_ptr<C>>时,在销毁向量时我有分割错误。

** glibc detected *** ./a.out: free(): invalid pointer: 0x0000000009948018***

为什么有区别?对我来说,即使是第一个实现也是有问题的。

#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;
};

应该将析构函数声明为虚函数。否则,如果使用指向C的指针删除类D,则将调用~C()析构函数,并且将错过清理的重要部分。

还要注意,在您的第二部分(不使用unique_ptr),您做了一些对象切片。这意味着您正在将类型为Ds1的副本创建为类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;
}

参见

  • 何时使用虚析构函数?
  • 什么是对象切片?

注意

如注释中所述,如果将析构函数标记为虚函数,则每个派生类也将具有虚析构函数。把它写在任何地方都可以使它更明确。这是一个风格问题。

你的"这很好"的例子做切片,向量只包含C的实例,这就是为什么它"工作",但不做你所期望的。正如dkg指出的那样,解决方案是使用虚拟医生。