多重继承和 unique_ptr 破坏
Multiple inheritance and unique_ptr destruction
我有经典的(可能有问题的)多重继承菱形方案。
- B继承A
- C继承A
- D继承了C和B
我想要一个可以包含 C
或 D
的 std::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
的指针删除了 class D
,那么将调用 ~C()
析构函数并且将错过清理的重要部分。
另请注意,在第二部分(不使用 unique_ptr
)中,您进行了一些对象切片。这意味着您正在将类型 D
的 s1
的副本创建到 class 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;
}
另见
备注
如评论中所述,如果将析构函数标记为虚拟,则每个派生的 class 也将具有虚拟析构函数。
把它写在任何地方都可以使它更明确。这是风格问题。
您的 "this is fine" 示例进行切片,向量仅包含 C
的实例,这就是为什么它 'works' 但没有按照您的预期进行。正如 dkg 指出的那样,解决方案是使用虚拟 dtors。
我有经典的(可能有问题的)多重继承菱形方案。
- B继承A
- C继承A
- D继承了C和B
我想要一个可以包含 C
或 D
的 std::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
的指针删除了 class D
,那么将调用 ~C()
析构函数并且将错过清理的重要部分。
另请注意,在第二部分(不使用 unique_ptr
)中,您进行了一些对象切片。这意味着您正在将类型 D
的 s1
的副本创建到 class 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;
}
另见
备注
如评论中所述,如果将析构函数标记为虚拟,则每个派生的 class 也将具有虚拟析构函数。 把它写在任何地方都可以使它更明确。这是风格问题。
您的 "this is fine" 示例进行切片,向量仅包含 C
的实例,这就是为什么它 'works' 但没有按照您的预期进行。正如 dkg 指出的那样,解决方案是使用虚拟 dtors。