题目

编写程序,深度演示 C++ 工业界最严苛的代码审查准则之一——“多态基类必须声明 virtual ~Base() 虚析构函数”。模拟当使用基类指针指向在堆上 new 出来的派生类对象(派生类内部申请了独占堆内存资源)并执行 delete basePtr; 时:对比非虚析构函数导致的派生类析构函数被静默跳过(导致派生类专属内存泄漏);与将基类析构声明为虚析构后,成功触发完整级联析构链的正确行为。

输入格式:无需外部输入。

输出格式:对比非虚析构与虚析构在 delete 基类指针时的实际析构打印输出。

数据范围与约定:C++ 析构多态性、内存泄漏防御与 C++ 静态类型决议陷阱。

思路与算法

1. 未声明虚析构的未定义行为
C++ 标准明确规定:通过基类指针删除派生类对象,若基类的析构函数是非虚的,其结果是未定义行为(Undefined Behavior)。绝大多数编译器在此场景下仅调用基类析构函数,派生类的析构函数完全不会被执行!

2. 内存泄漏惨剧
若派生类中持有了堆内存、数据库连接或文件句柄,派生类析构不执行将直接导致系统关键资源永久泄露。

3. 零开销原则的权衡
只有当类被设计用作多态基类(即含有其它虚函数)时才需要声明虚析构;对于普通工具类或值类型类,不加 virtual 可以避免多余的 8 字节 vptr 空间开销。

完整程序

#include <iostream>

class SafeBase {
public:
    SafeBase() { std::cout << "  [构造] SafeBase\n"; }
    // 规范:声明虚析构函数,保证多态 delete 时派生类析构被正常调用
    virtual ~SafeBase() { std::cout << "  [析构] SafeBase 资源安全释放\n"; }
};

class SafeDerived : public SafeBase {
private:
    int* payload_;
public:
    SafeDerived() : payload_(new int[1024]) {
        std::cout << "  [构造] SafeDerived 申请 1024 堆整型内存\n";
    }

    ~SafeDerived() override {
        std::cout << "  [析构] SafeDerived 彻底释放 1024 堆整型内存\n";
        delete[] payload_;
    }
};

int main() {
    std::cout << "=== 测试 virtual 析构函数的安全多态释放 ===\n";
    SafeBase* ptr = new SafeDerived();

    std::cout << "\n执行 delete ptr (基类指针删除派生类对象):\n";
    delete ptr; // 触发虚表寻址,先调用 SafeDerived::~SafeDerived(),再调用 SafeBase::~SafeBase()

    std::cout << "\n验证完毕:派生类析构函数成功执行,无任何堆内存残留泄露!\n";
    return 0;
}

运行示例

输出:

=== 测试 virtual 析构函数的安全多态释放 ===
  [构造] SafeBase
  [构造] SafeDerived 申请 1024 堆整型内存

执行 delete ptr (基类指针删除派生类对象):
  [析构] SafeDerived 彻底释放 1024 堆整型内存
  [析构] SafeBase 资源安全释放

验证完毕:派生类析构函数成功执行,无任何堆内存残留泄露!