09 December 2022

C++ 核心指南目录

“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

注意

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

强化

  • 一个类如果有虚函数,则应该有一个析构函数,要么是公开的虚函数,要么是保护的非虚函数。
  • 标记删除一个有虚函数,但是没有虚析构函数的类的对象的操作