前提知识:

对象的内存中只包含成员变量,存储在栈区或堆区(使用 new 创建对象);

成员函数与对象内存分离,存储在代码区

对象的大小,可以自己分析,int 四个字节,指针也是四个字节。(在x86中)可用sizeof运算符查看对象的大小。

1、普通继承

代码示例

class A {
public:int ma;void func(){cout << "A :: func" << endl;}
};class B : public A {
public:int mb;void func() {cout << "B :: func" << endl;}
};

内存分布

class A size(4):+---0      | ma+---class B size(8):+---0      | +--- (base class A)0      | | ma| +---4      | mb+---

2、普通多继承

代码示例

派生类都只有一个基类,称为单继承。C++ 也支持多继承,即一个派生类可以有两个或多个基类。

class A {
public:int ma;void func(){cout << "A :: func" << endl;}
};class B : public A {
public:int mb;void func() {cout << "B :: func" << endl;}
};class C : public A {
public:int mc;void func() {cout << "C :: func" << endl;}
};class D : public B, public C {
public:int md;void func() {cout << "D :: func" << endl;}
};

内存分布

从多继承的内存分布中,我们可以看到存在一些缺点:在一个派生类中保留间接基类的多份同名成员;菱形继承中,会有命名冲突的出现

类 A 派生出类 B 和类 C,类 D 继承自类 B 和类 C,这个时候类 A 中的成员变量继承到类 D 中变成了两份,一份来自 A-->B-->D 这条路径,另一份来自 A-->C-->D 这条路径。此时,在D中对A中的变量赋值,就会有命名冲突。并且在使用基类A的公开成员函数时,也会指向不明确报错。

class A size(4):+---0      | ma+---class B size(8):+---0      | +--- (base class A)0      | | ma| +---4      | mb+---class C size(8):+---0      | +--- (base class A)0      | | ma| +---4      | mc+---class D size(20):+---0      | +--- (base class B)0      | | +--- (base class A)0      | | | ma| | +---4      | | mb| +---8      | +--- (base class C)8      | | +--- (base class A)8      | | | ma| | +---
12      | | mc| +---
16      | md+---

3、虚继承

为了解决多继承时的命名冲突和冗余数据问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员。

在继承方式前面加上 virtual 关键字就是虚继承;

        虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类,本例中的 A 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。

代码示例

class A {
public:int ma;void func(){cout << "A :: func" << endl;}
};class B :virtual public A {
public:int mb;void func() {cout << "B :: func" << endl;}
};class C : virtual public A {
public:int mc;void func() {cout << "C :: func" << endl;}
};class D : public B, public C {
public:int md;void func() {cout << "D :: func" << endl;}
};

内存分布

在MSVC编译器中,引入虚基类表,如果某个派生类有一个或者多个虚基类,编译器会在派生类对象中安插一个指针(vbptr)指向虚基类表(vbtable)。虚基类表放的是偏移量。

class A size(4):+---0      | ma+---class B size(12):+---0      | {vbptr}4      | mb+---+--- (virtual base A)8      | ma+---B::$vbtable@:0      | 01      | 8 (Bd(B+0)A)class C size(12):+---0      | {vbptr}4      | mc+---+--- (virtual base A)8      | ma+---C::$vbtable@:0      | 01      | 8 (Cd(C+0)A)class D size(24):+---0      | +--- (base class B)0      | | {vbptr}4      | | mb| +---8      | +--- (base class C)8      | | {vbptr}
12      | | mc| +---
16      | md+---+--- (virtual base A)
20      | ma+---D::$vbtable@B@:0      | 01      | 20 (Dd(B+0)A)D::$vbtable@C@:0      | 01      | 12 (Dd(C+0)A)

4、虚函数

如果一个类包含了虚函数,那么在创建该类的对象时就会额外地增加一个数组,数组中的每一个元素都是虚函数的入口地址。每定义的任何一个对象,都会有vfptr,vfptr指向的是同一块vftable。

子类继承了父类之后,同名函数(包括返回值,函数名,参数列表)也会自动是virtual类型。子类的vftable中的函数,只有 virtual类型,不是virtual类型的,不会在vftable中。

代码示例

class A {
public:int ma;virtual void func() {cout << "A" << endl;}
};
class B : public A {
public:int mb;void func() {cout << "B" << endl;}void func2() {cout << "B" << endl;}virtual void func3() {}
};

内存分布

class A size(8):+---0      | {vfptr}4      | ma+---A::$vftable@:| &A_meta|  00      | &A::funcclass B size(12):+---0      | +--- (base class A)0      | | {vfptr}4      | | ma| +---8      | mb+---B::$vftable@:| &B_meta|  00      | &B::func1      | &B::func3

5、虚继承(多)、虚函数

代码示例

class A {
public:int ma;virtual void func(){cout << "A :: func" << endl;}
};class B :virtual public A {
public:int mb;void func() {cout << "B :: func" << endl;}
};class C : virtual public A {
public:int mc;void func() {cout << "C :: func" << endl;}
};class D : public B, public C {
public:int md;void func() {cout << "D :: func" << endl;}
};

内存分布

class A size(8):+---0      | {vfptr}4      | ma+---A::$vftable@:| &A_meta|  00      | &A::funcclass B size(16):+---0      | {vbptr}4      | mb+---+--- (virtual base A)8      | {vfptr}
12      | ma+---B::$vbtable@:0      | 01      | 8 (Bd(B+0)A)B::$vftable@:| -80      | &B::funcclass C size(16):+---0      | {vbptr}4      | mc+---+--- (virtual base A)8      | {vfptr}
12      | ma+---C::$vbtable@:0      | 01      | 8 (Cd(C+0)A)C::$vftable@:| -80      | &C::funcclass D size(28):+---0      | +--- (base class B)0      | | {vbptr}4      | | mb| +---8      | +--- (base class C)8      | | {vbptr}
12      | | mc| +---
16      | md+---+--- (virtual base A)
20      | {vfptr}
24      | ma+---D::$vbtable@B@:0      | 01      | 20 (Dd(B+0)A)D::$vbtable@C@:0      | 01      | 12 (Dd(C+0)A)D::$vftable@:| -200      | &D::func

6、多态的指向

在多态中,基类指针指向派生类对象时候,永远指向的是派生类基类部分数据的起始地址

父类并不能访问子类中在父类未出现的函数和变量。只能访问虚函数表中的函数。

7、vbptr 与 vfptr 优先问题

1、只有 虚继承。

2、基类有虚函数

3、子类有新的虚函数

继承、虚继承、虚函数内存分布(MSVC下)相关推荐

  1. C++虚继承的实现原理、内存分布、作用

    虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝.这将存在两个问题:其一,浪费存储空间:第二,存在二义性问题,通常可以将派生类对象的地址赋值给基类对象,实现 ...

  2. C++中类的数据成员和成员函数内存分布情况

    C++类是由结构体发展得来的,所以他们的成员变量(C语言的结构体只有成员变量)的内存分配机制是一样的.下面我们以类来说明问题,如果类的问题通了,结构体也也就没问题啦.类分为成员变量和成员函数,我们先来 ...

  3. 内存首地址为1000h_C++虚继承,菱形继承,内存分布

    前言 在叙述C++虚继承之前,我先给大家抛出一个问题.例如现在有4个类,分别是class A, class B, class C, class D.它们的关系如下图. 如上如所示,class B和cl ...

  4. C++虚函数表,虚表指针,内存分布

    虚函数表和内存分布那一块转载自:https://blog.twofei.com/496/ 虚函数效率转载自:https://www.cnblogs.com/rollenholt/articles/20 ...

  5. arm32 linux 内存分布,gcc代码反汇编查看内存分布[2]: arm-linux-gcc

    arm-none-linux-gnueabi-gcc -v gcc version 4.4.1 (Sourcery G++ Lite 2010q1-202) 重点: 代码中的内存分配, 地址从低到高: ...

  6. C++类内存分布——深度理解继承与虚函数

    1.前言与准备 工欲善其事,必先利其器,我们先用好Visual Studio工具,像下面这样一步一步来:       先选择左侧的C/C++->命令行,然后在其他选项这里写上/d1 report ...

  7. C++内存分布之菱形继承(无虚函数)

    菱形继承的定义是:两个子类继承同一父类,而又有子类同时继承这两个子类.例如a,b两个类同时继承c,但是又有一个d类同时继承a,b类.探究的过程还是很有趣的. 菱形继承的内存布局探究花了我几天时间,探究 ...

  8. C++中虚函数工作原理和(虚)继承类的内存占用大小计算

    转载请标明出处,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7883531 一.虚函数的工作原理       虚函数的实现要求对象携带额 ...

  9. 多重继承和虚继承的内存布局

    这篇文章主要讲解虚继承的C++对象内存分布问题,从中也引出了dynamic_cast和static_cast本质区别.虚函数表的格式等一些大部分C++程序员都似是而非的概念.原文见这里 (By Eds ...

最新文章

  1. Eclipse 中 Debug 模式跳转到 exitCurrentThread 的问题解决
  2. Rsync命令参数详解
  3. 常用几种数据库连接字符串
  4. linux 基因组数据下载,linux下用Aspera从NCBI上下载SRA格式宏基因组数据
  5. Nginx -静态资源Web服务
  6. 十强决赛即将拉开帷幕!TECHSPARK星星之火IT创新大赛诚邀您观赛
  7. 实时计算轻松上手,阿里云DataWorks Stream Studio正式发布
  8. ORACLE 数据库的级联查询 一句sql搞定(部门多级)
  9. C#类对象转换成XML
  10. java vtd-xml_XML解析技术之VTD-XML 简介及代码实例
  11. rgba通道转rgb,将RGBA颜色转换为RGB
  12. 微信发ascii_微信翻译竟能识别神秘代码!这里有份超全的彩蛋总结 | 晓技巧
  13. php制作600行表格,表格排版的基本操作
  14. 由浅入深玩转华为WLAN—12安全认证配置(5)Portal认证,外置Protal服务器TSM对接
  15. mui.fire运用
  16. TunesKit Audio Converter for Mac(音频格式转换软件)
  17. 银行排队信息预测系统数学建模
  18. 软考论文分享--论项目的沟通管理
  19. shell脚本自动化创建虚拟机的基本配置--tomcat--mysql--jdk--maven---妈妈再也不用担心我不会配置虚拟机了!
  20. Windows server 2012 R2 DHCP主从热备配合华为交换机DHCP中继配置详解(非域控版本)

热门文章

  1. 偶尔可以停下来休息,但是千万别蹲下来张望
  2. 被房子掏空的中国人开始消费降级了
  3. android手机耗电快怎么办,手机耗电快怎么办、怎么解决?简单几步帮你延长手机续航...
  4. 用maxscript处理顶点颜色勾边
  5. 纪念伏尔泰逝世一百周年的演说
  6. Hive tpcds - 3 测试
  7. 29岁年轻人离奇死亡:不是生活太苦,是你容易想得太多
  8. kotlin设置按钮不可点击_跟编程探索家学APP开发:设置APP首页的基础结构
  9. js中两个对象数组如何比对合并
  10. 我的100个工作基本