众所周知的情景:
#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就不会发现这样的错误。
是否有一些工具可以捕获这样的错误,至少对于调试版本,或者某些习惯用来检测指定类的错误?
答案 0 :(得分:5)
对于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 - 壬虚拟析构函数] 删除__ptr;
顺便说一句。您的示例类在基类中至少缺少一个虚函数。
[编辑]
将其变为错误使用:
-Werror=delete-non-virtual-dtor -Wsystem-headers
答案 1 :(得分:1)
我强烈认为,应该严格区分“以价值为基础”的课程和“以OO为基础”的课程(缺乏更好的学期)。
基于值的类应该没有公共基础,并且通常应该支持复制语义(除非专门设计为禁用复制)。这些类的对象除了它们的值之外没有其他身份。它们可与副本互换。
基于OO的类应该具有虚拟析构函数,并且永远不应该公开访问复制成员。只能通过虚拟clone
方法复制此类的对象。这些对象具有与其值不同的身份。
不应该有一个公共基础的类具有非虚拟析构函数或公共副本/移动ctor或公共副本/移动赋值运算符(在基础中)。
如果保持这种分离,通过非多态基指针对象切片或删除都不会发生意外。
不幸的是,没有工具(我知道)有助于保持这种分离。所以你需要在继承的同时进行尽职调查。这很简单。它是否有虚拟dtor 和无法访问/删除的副本ctor和副本分配?你可以公开继承。没有?避免潜在的混乱,使用组合或私人继承。
为继承而设计的优秀类将保护其复制成员,以便于在后代类中进行克隆。
不幸的是,对于第三方类,您没有选择权,因为作者通常会将复制成员公开,因此仍存在对象切片的风险。但是你不会有通过基指针不正确删除的风险。
TL; DR没有工具,只有程序员的尽职调查。