源码

#include <iostream>class Base1
{
public:virtual void func_1_1(){ std::cout << "Base1::func_1_1()" << std::endl; }virtual void func_1_2(){ std::cout << "Base1::func_1_2()" << std::endl; }virtual ~Base1() {}
};class Base2
{
public:virtual void func_2_1(){ std::cout << "Base2::func_2_1()" << std::endl; }virtual void func_2_2(){ std::cout << "Base1::func_2_2()" << std::endl; }virtual ~Base2() {}
};class Son :public Base1, public Base2
{
public:virtual void func_s(){ std::cout << "Son::func_s()" << std::endl; }virtual void func_2_1(){ std::cout << "Son::func_2_1()" << std::endl; }
};int main()
{Base2 *pb2 = new Son();delete pb2;return 0;
}

分析

Son类的内存布局如下所示:

在代码 delete pb2; 处打下断点,进入汇编,如下图所示:

总结过程,如下:

delete pb2 时,根据 Base2 的虚函数表,找到 trunk 项,从而获得了子类对象的首地址,并且调用子类对象的虚析构函数。然后先后执行了 Base2、Base1和Son类的虚构函数,最后完成 new Son 对象的释放。

若是如下代码:

    Base1 *pb1 = new Son();delete pb1;

则 Base1 虚函数表中的 trunk 项对应的代码中就不包含了调整 pb1 指针的位置,因为 pb1 本身就指向了 Son 类对象的首地址,所以直接调用了 Son 类对象的析构函数。

拓展:

1、虚函数表中的 trunk 项,实际上一段汇编代码的首地址,执行的内容如下:

  • 将 pb2 偏移到子类对象的首地址。
  • 执行子类对象的虚析构函数。

2、上述释放内存的成功实现的前提是,基类的析构函数必须是虚函数。若不是,则直接执行了 Base2 那部分的析构函数,这就导致了 Son 类对象时只释放 Base2 对应的那部分。

对于操作系统的内存分配机制来说,相对于内存块前面的固定位置会记录着该内存块的信息,包括该内存块的字节数。由于上述操作中,释放 Base2 对应的部分是时候,没有办法找到这部分的内存信息,从而导致了释放内存失败,程序运行崩溃。

(SAW:Game Over!)

Cpp 对象模型探索 / 多重继承下基类指针释放子类对象的原理说明(虚析构函数的作用)相关推荐

  1. 基类指针和子类指针相互赋值

    首先,给出基类animal和子类fish [cpp] view plaincopy print? //================================================= ...

  2. Cpp 对象模型探索 / 单一继承的类的内存布局

    目录 1.父类和子类都没有虚函数 2.父类有虚函数.子类没有虚函数 3.父类没有虚函数,子类有虚函数 4.父类和子类都有虚函数 5.总结 #include <iostream> class ...

  3. Cpp 对象模型探索 / 多重继承虚函数表分析

    一.源码 #include <iostream>class Base1 { public:virtual void func11(){std::cout << "Ba ...

  4. 基类指针调用派生类函数_C++ 多态性:虚函数--基类与派生类类型转换(第7章 05)例子问题解析(学习笔记:第8章 05)...

    虚函数[1] 问题:还记得第7章的例子吗[2]? 例7-3 类型转换规则举例 #include <iostream> using namespace std; class Base1 { ...

  5. Cpp 对象模型探索 / 不能被继承的类

    两种方法 C++ 11 final 关键字 友元类 + 虚继承. 栗子 class A { private:A() {}friend class B; }; class B : virtual pub ...

  6. 虚析构函数? vptr? 指针偏移?多态数组? delete 基类指针 内存泄漏?崩溃?...

    五条基本规则: 1.如果基类已经插入了vptr, 则派生类将继承和重用该vptr.vptr(一般在对象内存模型的顶部)必须随着对象类型的变化而不断地改变它的指向,以保证其值和当前对象的实际类型是一致的 ...

  7. Cpp 对象模型探索 / 含有虚基类的类的内存布局

    一.栗子 class Grand { public:int i_grand_ = 8; };class Parent1 : public virtual Grand { public:int i_pa ...

  8. Cpp 对象模型探索 / 带有虚继承类的构造函数的调用顺序

    栗子 #include <iostream> class A { public:A() { std::cout << "A" << std::e ...

  9. C++ day22 继承(二)基类指针数组通过虚方法实现智能的多态

    继承一共有三种: 公有继承 私有继承 保护继承 文章目录 公有继承 基类和派生类的关系 is-a(用公有继承表示"是一种"的关系) has-a uses-a is-like-a i ...

最新文章

  1. jet nano 车道识别
  2. rust的权限柜怎么做_潍坊装修知识~二胎家庭不做上下铺,把两张床靠墙放,中间做收纳柜,你感觉怎么样?...
  3. 前端学习(2424):关于问题的解决方式
  4. 变更数据推送java_idea 团队成员修改工程后push推送
  5. 两个整数求平均值,防止溢出的问题
  6. Net设计模式实例之观察者模式(Observer Pattern)
  7. Unix 下获得 root权限
  8. 案例实操-Top10热门品类
  9. 人口logistic模型公式_人口指数增长模型和Logistic模型
  10. 《心经》-翻译、中英梵对照
  11. juniper设备配置syslog日志发送到远程日志服务器
  12. 全连接网络实现Fashion数据集学习/预测
  13. “单向网闸”技术介绍
  14. jadx工具介绍及使用
  15. 在Excel中将人民币金额小写转成大写(转)
  16. CSDN 博客被自己误删了怎么办---(联系QQ客服)
  17. httpclient Accept-Encoding 乱码
  18. 一文读懂 协方差矩阵
  19. 广东省谷歌地球高程DEM等高线下载
  20. 李刚疯狂java抄袭,推荐:疯狂java讲义--李刚著作(3)

热门文章

  1. 获取IOS应用的子目录
  2. 日常检查IBM P系列小型机状态的项目及其相关命令
  3. [zz]grep 命令的使用
  4. Android HelloWorld 例子
  5. linux常用命令(1)帮助命令man使用
  6. 小心陷入MySQL索引的坑
  7. 通过Athens搭建go私服
  8. linux md5sum获取文件指纹数值并使用cut切割
  9. linux截图快捷键
  10. Scala元组数据的遍历