09 December 2022

C++ 核心指南目录

C.127: A class with a virtual function should have a virtual or protected destructor

理由

类如果有虚函数,那么我们经常是通过其基类类型的指针间接访问使用这个类。最后用户必须调用 delete 删除其基类类型的指针。而且,我们通常会通过其基类的智能指针使用该类。因此,基类的析构函数应该是公开的虚函数。

不常见的情况是,如果不允许通过基类的指针删除对象,可以将析构函数设置为保护的非虚函数。see C.35

例子

struct B {
    virtual int f() = 0;
    // ... no user-written destructor, defaults to public non-virtual ...
};

// bad: derived from a class without a virtual destructor
struct D : B {
    string s {"default"};
    // ...
};

void use()
{
    unique_ptr<B> p = make_unique<D>();
    // ...
} // undefined behavior, might call B::~B only and leak the string

注意

很多人不遵循本条建议,他们计划只通过共享指针shared_ptr使用这些类,如 std::shared_ptr<B> p = std::make_shared<D>(args) 。因为共享指针会处理好删除操作,所以不会导致不恰当的使用基类的 delete 而造成的内存泄漏。人们习惯了这个操作,会产生一种不会出错的假象。但是这条指南很重要。如果有人使用make_unique分配内存怎么办?这样会有不安全的行为。除非 B 的作者确保别人不会误用 B ,比如将所有的构造函数设置为 private ,并提供一个工厂函数,确保通过make_shared分配资源。

强化

  • 类如果有虚函数,则应该有一个析构函数,要么是公开的虚函数,要么是保护的非虚函数。
  • 标记这类情况:删除一个类对象的时候用了 delete 而这个类有虚函数,但是没有虚析构函数。