众所周知的场景:
#include <memory>
class A{};
class B : public A {};
int main()
{
std::unique_ptr<A> a = std::make_unique<B>();
// bam, when a gets deleted, we have undefined behavior.
return 0;
}
此外,如果A
和B
的大小相同,即使是Valgrind也无法捕捉到这样的错误。
有没有一些工具可以捕捉到这样的错误,至少在调试构建中是这样,或者有没有一些习惯用法可以检测指定类的这样的错误?
对于gcc,您可以指定:
-Wdelete-non-virtual-dtor -Wsystem-headers
要查看delete-non-virtual-dtor
警告生成的by std::default_delete
,它将如下所示:
/usr/local/include/c++/5.3.0/bits/unique_ptr.h:76:2:警告:删除具有非虚拟析构函数的多态类类型"B"的对象可能会导致未定义的行为[-Wdelete非虚拟dtor]删除__ptr;
实时
顺便说一句。您的示例类在基类中至少缺少一个虚拟函数。
[编辑]
将其转化为错误使用:
-Werror=delete-non-virtual-dtor -Wsystem-headers
我强烈认为应该严格区分"基于值"的类和"基于OO"的类(因为没有更好的术语)。
基于值的类不应该有公共基,并且通常应该支持复制语义(除非专门设计为禁用复制)。这些类的对象除了其值之外没有任何身份。它们可以与复制品互换。
基于OO的类应该具有虚拟析构函数,并且不应该具有可公开访问的复制成员。此类对象只能通过虚拟clone
方法进行复制。这些对象具有与其值分离的身份。
不应该有任何具有公共基的类具有非虚拟析构函数、公共复制/移动构造函数或公共复制/移赋值运算符(在基中)。
如果保持这种分离,那么通过非多态基指针进行对象切片或删除就不会发生意外。
不幸的是,(据我所知)没有任何工具可以帮助保持这种分离。因此,您需要在继承时尽职调查。这真的很简单。它是否有一个虚拟dtor和不可访问/已删除的副本ctor和副本分配?你可以公开继承。不避免潜在的混乱,使用组合或私有继承。
为继承而设计的好类会保护其复制成员,以便在子类中进行克隆。
不幸的是,对于第三方类,您别无选择,因为作者通常会将复制成员公开,因此仍然存在对象切片的风险。但是,您将不会有通过基指针进行不当删除的风险。
TL;DR没有工具,只有程序员的尽职调查。