有没有办法检测是否使用非虚拟基础析构函数错误地删除了一个类?

时间:2016-03-27 15:10:19

标签: c++

众所周知的情景:

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

此外,只要AB的大小相同,Valgrind就不会发现这样的错误。

是否有一些工具可以捕获这样的错误,至少对于调试版本,或者某些习惯用来检测指定类的错误?

2 个答案:

答案 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;

live

顺便说一句。您的示例类在基类中至少缺少一个虚函数。

[编辑]

将其变为错误使用:

 -Werror=delete-non-virtual-dtor -Wsystem-headers 

答案 1 :(得分:1)

我强烈认为,应该严格区分“以价值为基础”的课程和“以OO为基础”的课程(缺乏更好的学期)。

基于值的类应该没有公共基础,并且通常应该支持复制语义(除非专门设计为禁用复制)。这些类的对象除了它们的值之外没有其他身份。它们可与副本互换。

基于OO的类应该具有虚拟析构函数,并且永远不应该公开访问复制成员。只能通过虚拟clone方法复制此类的对象。这些对象具有与其值不同的身份。

不应该有一个公共基础的类具有非虚拟析构函数或公共副本/移动ctor或公共副本/移动赋值运算符(在基础中)。

如果保持这种分离,通过非多态基指针对象切片或删除都不会发生意外。

不幸的是,没有工具(我知道)有助于保持这种分离。所以你需要在继承的同时进行尽职调查。这很简单。它是否有虚拟dtor 无法访问/删除的副本ctor和副本分配?你可以公开继承。没有?避免潜在的混乱,使用组合或私人继承。

为继承而设计的优秀类将保护其复制成员,以便于在后代类中进行克隆。

不幸的是,对于第三方类,您没有选择权,因为作者通常会将复制成员公开,因此仍存在对象切片的风险。但是你不会有通过基指针不正确删除的风险。

TL; DR没有工具,只有程序员的尽职调查。