摘自Jerry19880126

简单类

class Base
{
int a;
int b;
public:
void CommonFunction();
};

简单类继承

class DerivedClass: public Base
{
int c;
public:
void DerivedCommonFunction();
};

带有虚函数的类

class Base
{
int a;
int b;
public:
void CommonFunction();
void virtual VirtualFunction();
};

虚函数类继承

没有复写的继承

 class DerivedClass: public Base
{
int c;
public:
void DerivedCommonFunction();
void virtual VirtualFunction();
};

复写的继承

class DerivedClass1 : public Base
{
int c;
public:
void DerivedCommonFunction();
void virtual VirtualFunction();
void virtual VirtualFunction2();
};

多重继承

class Base
{
int a;
int b;
public:
void CommonFunction();
void virtual VirtualFunction();
}; class DerivedClass1: public Base
{
int c;
public:
void DerivedCommonFunction();
void virtual VirtualFunction();
}; class DerivedClass2 : public Base
{
int d;
public:
void DerivedCommonFunction();
void virtual VirtualFunction();
}; class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
int e;
public:
void DerivedDerivedCommonFunction();
void virtual VirtualFunction();
};

内存分布从父类到子类,依次如下:

Base中有一个虚表指针

DerivedClass1继承了Base,内存排布是先父类后子类。

DerivedClass2的情况是类似于DerivedClass1的。

DerivedDerivedClass,由外向内看,它并列地排布着继承而来的两个父类DerivedClass1与DerivedClass2,还有自身的成员变量e。DerivedClass1包含了它的成员变量c,以及Base,Base有一个0地址偏移的虚表指针,然后是成员变量a和b;DerivedClass2的内存排布类似于DerivedClass1,注意到DerivedClass2里面竟然也有一份Base。

这里有两份虚表了,分别针对DerivedClass1与DerivedClass2,在&DerivedDericedClass_meta下方的数字是首地址偏移量,靠下面的虚表的那个-16表示指向这个虚表的虚指针的内存偏移,这正是DerivedClass2中的{vfptr}在DerivedDerivedClass的内存偏移。

虚继承

class DerivedClass1: virtual public Base
{
int c;
public:
void DerivedCommonFunction();
void virtual VirtualFunction();
}; class DerivedClass2 : virtual public Base
{
int d;
public:
void DerivedCommonFunction();
void virtual VirtualFunction();
}; class DerivedDerivedClass : public DerivedClass1, public DerivedClass2
{
int e;
public:
void DerivedDerivedCommonFunction();
void virtual VirtualFunction();
};

Base类没有变化

DerivedClass1就已经有变化了,原来是先排虚表指针与Base成员变量,vfptr位于0地址偏移处;但现在有两个虚表指针了,一个是vbptr,另一个是vfptr。vbptr是这个DerivedClass1对应的虚表指针,它指向DerivedClass1的虚表vbtable,另一个vfptr是虚基类表对应的虚指针,它指向vftable。

下面列出了两张虚表,第一张表是vbptr指向的表,8表示{vbptr}与{vfptr}的偏移;第二张表是vfptr指向的表,-8指明了这张表所对应的虚指针位于内存的偏移量。

DerivedClass2的内存分布类似于DerivedClass1,同样会有两个虚指针,分别指向两张虚表(第二张是虚基类表)。

下面来仔细看一下DerivedDerivedClass的内存分布,这里面有三个虚指针了,但base却只有一份。第一张虚表是内含DerivedClass1的,20表示它的虚指针{vbptr}离虚基表指针{vfptr}的距离,第二张虚表是内含DerivedClass2的,12表示它的虚指针{vbptr}离虚基表指针{vfptr}的距离,最后一张表是虚基表,-20指明了它对应的虚指针{vfptr}在内存中的偏移。





虚继承的作用是减少了对基类的重复,代价是增加了虚表指针的负担(更多的虚表指针)。

下面总结一下(当基类有虚函数时):

1. 每个类都有虚指针(vptr, virtual table pointer)和虚函数表(vtbl, virtual table);
2. 如果不是虚继承,那么子类将父类的虚指针继承下来,并指向自身的虚函数表(发生在对象构造时)。有多少个虚函数,虚函数表里面的项就会有多少。多重继承时,可能存在多个的基类虚函数表与虚指针;
3. 如果是虚继承,那么子类会有两份虚指针,一份指向自己的虚函数表,另一份指向基类虚函数表,多重继承时基类虚函数表与基类虚指针有且只有一份。

C++类的内存结构的更多相关文章

  1. Java内存结构、类的初始化、及对象构造过程

    概述 网上关于该题目的文章已经很多,我觉得把它们几个关联起来讲可能更好理解一下.与其它语言一样,它在执行我们写的程序前要先分配内存空间,以便于存放代码.数据:程序的执行过程其实依然是代码的执行及数据的 ...

  2. C++类对应的内存结构

    提示1:对“内存结构”表示有疑问或不解的,先参考: http://blog.csdn.net/guogangj/archive/2007/05/25/1625199.aspx, 本文使用的表示方法和V ...

  3. 关于结构体和C++类的内存地址问题

    关于结构体和C++类的内存地址问题   今天终于有时间写点东西了~ 太爽了  *_*   很多人都知道C++类是由结构体发展得来的,所以他们的成员变量(C语言的结构体只有成员变量)的内存分配机制是一样 ...

  4. jvm系列(二):JVM内存结构

    JVM内存结构 所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemoryError的异常到底涉及到运行时数据的哪块区域?该怎么解决呢?其实如果你经常解决服务器性能 ...

  5. JVM之内存结构

    JVM是按照运行时数据的存储结构来划分内存结构的.JVM在运行Java程序时,将他们划分成不同格式的数据,分别存储在不同的区域,这些数据就是运行时数据.运行时数据区域包括堆,方法区,运行时常量池,程序 ...

  6. Oracle之内存结构(SGA、PGA)

    一.内存结构 SGA(System Global Area):由所有服务进程和后台进程共享: PGA(Program Global Area):由每个服务进程.后台进程专有:每个进程都有一个PGA. ...

  7. 浅析JVM内存结构和6大区域(转)举例非常好

    内存作为系统中重要的资源,对于系统稳定运行和高效运行起到了关键的作用,Java和C之类的语言不同,不需要开发人员来分配内存和回收内存,而是由JVM来管理对象内存的分配以及对象内存的回收(又称为垃圾回收 ...

  8. [转]oracle学习入门系列之五内存结构、数据库结构、进程

    原文地址:http://www.2cto.com/database/201505/399285.html 1 Oracle数据库结构 关于这个话题,网上一搜绝对一大把,更别提书籍上出现的了,还有很多大 ...

  9. 虚函数列表: 取出方法 // 虚函数工作原理和(虚)继承类的内存占用大小计算 32位机器上 sizeof(void *) // 4byte

    #include <iostream> using namespace std; class A { public: A(){} virtual void geta(){ cout < ...

随机推荐

  1. Nginx 安装配置

    Nginx("engine x")是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器. ...

  2. 一个特别好用的属性:inline-block

    说起inline-block,大家都不陌生,如果我要保证:有一个内联元素,保证它换行时,不被截断,而要整体换行,那么设置:display:inline-block 即可

  3. 使用readAsDataURL方法预览图片

    使用FileReader接口的readAsDataURL方法实现图片的预览. 在FileReader出现之前,前端的图片预览是这样实现的:把本地图片上传到服务器,服务器把图片地址返回,并把它替换到图片 ...

  4. P1843 奶牛晒衣服(二分)

    思路:就是一个模板,只是找最小化而已.在判断函数里面:当湿度<=x*A不判断, 反之sum+=(a[i]-x*A)/B+(a[i]-x*A)%B?1:0; #include<iostrea ...

  5. jenkins经验

    https://blog.csdn.net/aixiaoyang168/article/details/80636544#31_61(转)

  6. Apollo内核版本安装

    参考:https://github.com/ApolloAuto/apollo/blob/master/docs/quickstart/apollo_software_installation_gui ...

  7. 环境部署(九):linux下安装python+chrome+Xvfb

    在基于selenium进行的UI自动化测试中,开发调试环境一般都是windows操作系统.完成后需要部署到专门的测试环境. 如要要部署到linux环境的服务器(阿里云.腾讯云)执行,那么测试脚本也需要 ...

  8. Numpy数组的保存与读取

    1. 数组以二进制格式保存 np.save和np.load是读写磁盘数组数据的两个主要函数.默认情况下,数组以未压缩的原始二进制格式保存在扩展名为npy的文件中,以数组a为例 np.save(&quo ...

  9. 如何选择分布式事务形态(TCC,SAGA,2PC,补偿,基于消息最终一致性等等)

    各种形态的分布式事务 分布式事务有多种主流形态,包括: 基于消息实现的分布式事务 基于补偿实现的分布式事务(gts/fescar自动补偿的形式) 基于TCC实现的分布式事务 基于SAGA实现的分布式事 ...

  10. 跨域访问-需要设置HTTP响应标头

    前提:服务端网站的配置(被请求的网站) 1.需要在IIS服务器站点的功能视图中设置HTTP响应标头: 2.双击“HTTP响应标头”进入设置界面 3.点击右侧添加按钮弹出窗口 4.填入需要设置的信息 名 ...