题目
编写程序,深度演示 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 资源安全释放
验证完毕:派生类析构函数成功执行,无任何堆内存残留泄露!