C++ 虚析构(virtual destructor)原理
注意:本文仅为个人理解,可能有误!
先看一段代码:
#include <iostream> using namespace std; class CBase{ public: CBase() { cout<<"CBase construct ... "<<endl; } virtual ~CBase() { cout<<"CBase destructor ... "<<endl; } }; class CSon : public CBase{ public: CSon(){ cout<<"CSon construct ... "<<endl; } ~CSon(){ cout<<"CSon destructor ... "<<endl; } }; void test() { CBase* pObj = new CSon(); delete pObj; } int main(int argc,char* argv[]) { test(); return 0; }
上面这段代码,输出:
CBase construct ...
CSon construct ...
CSon destructor ...
CBase destructor ...
大家都能理解,而将virtual ~CBase() 的virtual 去掉, 将输出:
CBase construct ...
CSon construct ...
CBase destructor ...
大家将看到,子类的析构函数没有被调用。
那为什么加了virtual就会调用子类的析构函数呢?这是本文的主题。
首先要理解几个概念:
0,在没有加virtual的时候,这个继承体系没有任何虚函数,所以CSon,CBase均不含虚函数表。
1,子类构造函数/析构函数会自动调用父类的构造/析构函数(编译期决定);
2,编译期的常识:
CBase* pObj = new CSon();
这句相当于
CSon* pTmp = new CSon();
CBase* pObj = (CBase*)pTmp;
前一句中编译器是直接调用CSon::CSon()返回位pTmp,pTmp赋值给pObj。因而生成的pObj实际上是CSon对象,但pObj的类型被记录为CBase类型。
delete pObj;
delete时编译器只知道这个pObj是CBase类型 。
情况一,在CBase的析构函数为非virtual时:
编译器在编译“delete pObj;” 时根据pObj的类型是CBase调用CBase::~CBase(),不会调用子类析构函数。
情况二,在CBase的析构函数为virtual时:
此时继承体系中有一个虚函数表,这个时候,编译器就不会直接调用CBase::~CBase(),而是调用call pObj->pvtable-> vtable[0],又pObj这个对象实际上是CSon对象,所以调用流程如下:
delete pObj; –> call pObj->pvtable-> vtable[0] –> CSon::~CSon()
而在CSon::~CSon中会自动递归调用父类的析构函数,所以全部资源释放完成。
关于动态绑定部分,可以参见如下两张图:
1,CBase初始化完毕:
2,CSon初始化完毕:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· [AI/GPT/综述] AI Agent的设计模式综述