深圳市住建局网站官网济南网站建设公司哪家好
Effective C++ 条款07:为多态基类声明virtual析构函数
核心思想:当通过基类指针删除派生类对象时,如果基类没有虚析构函数,会导致派生类资源泄漏。因为此时只会调用基类的析构函数,而不会调用派生类的析构函数。
⚠️ 1. 问题场景:非虚析构函数导致资源泄漏
class Base {
public:Base() { std::cout << "Base构造\n"; }~Base() { std::cout << "Base析构\n"; } // 非虚析构函数
};class Derived : public Base {
public:Derived() : data(new int(42)) { std::cout << "Derived构造\n"; }~Derived() { delete data; // 释放资源std::cout << "Derived析构\n"; }
private:int* data; // 派生类独占资源
};int main() {Base* pb = new Derived(); // 基类指针指向派生类对象delete pb; // 仅调用Base::~Base() → 内存泄漏!
}
输出结果:
Base构造
Derived构造
Base析构
问题:Derived的资源data未被释放 → 内存泄漏!
✅ 2. 解决方案:声明虚析构函数
class Base {
public:Base() { std::cout << "Base构造\n"; }virtual ~Base() { std::cout << "Base析构\n"; } // 虚析构函数
};class Derived : public Base { /* 实现同上 */ };int main() {Base* pb = new Derived();delete pb; // 正确调用派生类析构函数
}
输出结果:
Base构造
Derived构造
Derived析构  // 先调用派生类析构函数
Base析构    // 再调用基类析构函数
🔍 3. 关键原则
| 场景 | 析构函数要求 | 原因 | 
|---|---|---|
| 多态基类(有虚函数) | 必须为 virtual | 确保通过基类指针删除派生类对象时,正确调用派生类析构函数 | 
| 非多态基类(无虚函数) | 不应为 virtual | 避免虚表指针带来的空间开销(条款7指出每个对象增加4-8字节) | 
| STL容器(如 std::string) | 禁止继承 | 标准库类的析构函数均为非虚,通过基类指针删除派生类对象会导致未定义行为 | 
⚠️ 4. 错误实践:继承STL容器类
class MyString : public std::string { 
public:~MyString() { std::cout << "MyString析构\n"; }
};int main() {std::string* ps = new MyString(); delete ps; // 未定义行为!std::~string非虚
}
结果:MyString::~MyString()不会被调用 → 潜在资源泄漏!
💎 5. 纯虚析构函数的特殊用法
使类成为抽象类,同时仍需要提供实现
class AbstractBase {
public:virtual ~AbstractBase() = 0; // 纯虚声明
};
AbstractBase::~AbstractBase() {}  // 必须提供实现class Concrete : public AbstractBase {
public:~Concrete() override { std::cout << "Concrete析构\n"; }
};int main() {AbstractBase* p = new Concrete();delete p; // 正确调用链:Concrete::~ → AbstractBase::~ 
}
总结:多态基类虚析构三原则
- 多态基类必须声明虚析构函数
 virtual ~Base() = default;
- 非多态基类不要声明虚析构函数
 避免无谓的虚函数表开销
- 禁止继承无虚析构函数的类(如STL容器)
 组合优于继承:将目标类作为成员变量而非基类
