当前位置: 首页 > news >正文

Effective C++ 条款07:为多态基类声明virtual析构函数

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::~ 
}

总结:多态基类虚析构三原则

  1. 多态基类必须声明虚析构函数
    virtual ~Base() = default;
  2. 非多态基类不要声明虚析构函数
    避免无谓的虚函数表开销
  3. 禁止继承无虚析构函数的类(如STL容器)
    组合优于继承:将目标类作为成员变量而非基类
http://www.xdnf.cn/news/1206091.html

相关文章:

  • 【esp32s3】7 - VSCode + PlatformIO + Arduino + 构建项目
  • 前端高级综合搜索组件 SearchBox 使用详解!
  • 学习dify:一个开源的 LLM 应用开发平台
  • C#_运算符重载 operator
  • 【kafka】消息队列
  • Java 数学工具类 Math
  • redis未授权getshell四种方式
  • Leetcode——11. 盛最多水的容器
  • 利用DataStream和TrafficPeak实现大数据可观察性
  • 【Git】Linux-ubuntu 22.04 初步认识 -> 安装 -> 基础操作
  • Prompt工程记录
  • MCU+RTOS调试
  • STM32启动流程
  • opencv 模块裁剪 按需安装指定模块
  • MCU 中的 PWM(脉冲宽度调制)是什么?
  • 未授权访问复现
  • Python动态规划:从基础到高阶优化的全面指南
  • 未授权访问漏洞靶场(redis,MongoDB,Memcached...)
  • Unity_UI_NGUI_锚点组件
  • 项目如何按时交付?重点关注的几点
  • 【Linux操作系统】简学深悟启示录:Linux环境基础开发工具使用
  • GoLand 项目从 0 到 1:第三天 —— 图数据库版本管理方案调研与中间件部署
  • Dify-14: 工作流API端点
  • 在虚拟机ubuntu上修改framebuffer桌面不能显示图像
  • STM32F4—电源管理器
  • YOLOv11改进:添加SCConv空间和通道重构卷积二次创新C3k2
  • 时间数字转换器TDC的FPGA方案及核心代码
  • 数分思维10:用户增长
  • 小智源码分析——音频部分(二)
  • 机器学习sklearn:决策树的参数、属性、接口