C++ 继承、虚继承、虚函数类内存分配
来源:互联网 发布:关于大数据的新闻报道 编辑:程序博客网 时间:2024/05/24 05:24
目录:
一、单个类的大小
二、普通单继承
三、普通多继承
四、虚拟单继承
五、包含虚函数的普通、虚拟混合继承
六、包含虚函数的多重虚拟继承
一、单个类的大小
(1)真空类
class CNull{};
长度:1
内存结构:未知
注:长度其实为0,这个字节作为内容没有意义,可能每次都不一样。
(2)空类
class CNull2{public: void foo(){printf("foo()\n");}};
长度:1
内存结构:未知
注:内部的成员函数并不会影响类大小
(3)简单类
class COneMember{public: COneMember(int iValue = 0){m_iOne = iValue;}private: int m_iOne; //char ch;};
长度:4
内存结构:00 00 00 00 // m_iOne
注:成员数据影响类的大小。如果同时定义char ch,类的长度为8,考虑到对齐问题。
(4)包含static成员类
class CStaticNull{public: static void foo(){printf("foo()\n");} static int m_iValue;};
长度:1
内存结构:未知
注:static成员不会占用类的大小,static成员的存在区域为静态区,可以认为是”全局”的。
(5)带虚函数的空类
class CVirtualNull{public: virtual void foo(){printf("foo()\n");}};
长度:4
内存结构:
00 31 42 00 // 指向虚表的指针
00 42 31 00:(虚表)
41 10 40 00 // 指向虚函数foo的指针
00401041:
E9 78 02 00 00 E9 C3 03… // 函数foo的内容
注:带虚函数的类长度增加了4,就是一个指向虚函数表的指针。上面例子中,虚表中只有一个函数指针,值为0x00401041,指向的这个地址就是函数的入口。
二、普通单继承
(1)简单继承
class CTwoMember: public COneMember{private: int m_iTwo;};
长度:8
内存结构:
00 00 00 00 // m_iOne
CC CC CC CC //m_iTwo
注:子类成员接在父类成员之后
(2)再继承
class CThreeMember: public CTwoMember{public: CThreeMember(int iValue = 10){m_iThree = iValue;}private: int m_iThree;};
长度:12
内存结构:
00 00 00 00 // m_iOne
CC CC CC CC // m_iTwo
0A 00 00 00 //m_iThree
注:孙类成员接在子类之后,再再继承就依此类推了。
(3)继承带虚函数的类
class CVirtualDerived:public CVirtualNull{public: CVirtualDerived(){m_iVD = 0xFF;}; ~CVirtualDerived();private: int m_iVD; };
长度:8
内存结构:
3C 50 42 00 // 虚表指针
FF 00 00 00 // m_iVD
0042503C:(虚表)
23 10 40 00 // 指向虚函数foo的指针,如果这时候创建一个CVirtualNull对象,会发现它的虚表的内容跟这个一样。
注:父类带虚函数,子类就算没有显示声明虚函数,也会复制父类虚表。虚表存放的位置跟父类不同,但内容相同。
(4)子类有新的虚函数
class CVirtualDerived02:public CVirtualNull{public: CVirtualDerived02(){m_iVD = 0xFF;}; ~CVirtualDerived02(); virtual void foo2(){printf("foo()\n");};private: int m_iVD; };
长度:8
内存结构:
24 61 42 00 // 虚表指针
FF 00 00 00 // m_iVD
00426124:(虚表)
23 10 40 00 // 指向虚函数foo的指针
50 10 40 00 // 指向虚函数foo2的指针
注:虚表还是只有一张,新的虚函数指针将添加在从父类拷贝过来的虚表的后面。
(5)继承纯虚函数
class CPureVirtual{ virtual void foo() = 0;};class CDerivePV:public CPureVirtual{ void foo(){printf("vd:foo()\n");};};
长度:4(CPureVirtual),4(CDerivePV)
内存结构:
CPureVirtual:不可实例化
CDerivePV:
28 50 42 00 //虚表指针
00425028:(虚表)
5A 10 40 00 // 指向foo函数的指针
注:带纯虚函数的类为抽象类,不可实例化,由其派生类实现纯虚函数。
三、普通多继承
(1)多重继承
class ClassA{public: ClassA(int iValue = 1){m_iA = iValue;}private: int m_iA;};class ClassB{public: ClassB(int iValue = 2){m_iB = iValue;}private: int m_iB;};class ClassC{public: ClassC(int iValue = 3){m_iC = iValue;}private: int m_iC;};class CComplex:public ClassA, public ClassB, public ClassC{public: CComplex(int iValue = 4){m_iComplex = iValue;}private: int m_iComplex;};
长度:16
内存结构:
01 00 00 00 // m_iA
02 00 00 00 // m_iB
03 00 00 00 // m_iC
04 00 00 00 // m_iComplex
注:父类成员在前,子类成员在后
(2)更复杂的继承
长度:32
内存结构:
01 00 00 00 // m_iA
02 00 00 00 // m_iB
03 00 00 00 // m_iC
04 00 00 00 // m_iComplex
00 00 00 00 // m_iOne
CC CC CC CC // m_iTwo
0A 00 00 00 //m_iThree
05 00 00 00 // m_iVeryComplex
注:子成员放在父成员后
(3)虚函数类的多重继承
长度:24
内存结构:
F8 50 42 00 // 虚表指针
01 00 00 00 // m_iA
02 00 00 00 // m_iB
E8 50 42 00 // 虚表指针
03 00 00 00 // m_iC
04 00 00 00 // m_iComplex
004250F8:(虚表)
5A 10 40 00 //FooA
55 10 40 00 //FooB
64 10 40 00 //FooComplex
004250E8:(虚表)
5F 10 40 00 //FooC
注:子类的虚函数接在第一个基类的虚函数表的后面,所有B接在A后面,Complex接在B后面。
四、虚拟单继承
(1)简单虚拟继承
class CTwoMember:virtual public COneMember{private: int m_iTwo; };
长度:12
内存结构:
E8 2F 42 00 // 指针,指向一个关于偏移量的数组,且称之为虚基类偏移量表指针
CC CC CC CC // m_iTwo
00 00 00 00 // m_iOne (虚基类数据成员)
注:虚继承中子类长度增加了4,其实是多了一个指针。可以认为它指向一个关于虚基类偏移量的数组,偏移量是关于虚基类数据成员的偏移量。
(2)”闭合”虚继承
长度:24
内存结构:
14 30 42 00 // ClassB的虚基类偏移量表指针
02 00 00 00 // m_iB
C4 2F 42 00 // ClassC的虚基类偏移量表指针
03 00 00 00 // m_iC
04 00 00 00 // m_iComplex
01 00 00 00 // m_iA
注:虚基类的成员m_iA只出现了一次,并且在最后面。虚继承利用一个”虚基类偏移量表指针”来使用虚拟基类,即使被重复继承也只会出现一次。
(3)包含虚函数类的虚继承
长度:visual studio 8, 20, 32
gcc: 8,16,24
内存结构:
VirtualInheritance
// 虚表指针
// chVI[3]
sonClass1
// 虚表指针vpt_sonClass1 4
// chSC1[3] 4
// 指向父类的虚表指针vtp_sonClass1_VirtualInheritanc 4 (gcc中没有计算该虚类指针)
// 父类的大小 8
sonClass2
// 虚表指针vpt_sonClass2 4
// chSC2[3] 4
// 指向父类的虚表指针vtp_sonClass2_sonClass1 4 (gcc中没有计算该虚类指针)
// 父类的大小 20(16)
五、包含虚函数的普通、虚拟混合继承
长度:36(visual studio) 、28(gcc)
内存结构:
sonClass2
// 虚表指针vpt_sonClass2 4
// chSC2[3] 4
// 指向父类的虚表指针vtp_sonClass2_sonClass1 4 (gcc中没有计算该虚类指针)
// 父类的大小 20(16)
// chVI2[3] 4
六、包含虚函数的多重虚拟继承
长度:40
内存结构:
sonClass2
(1)自身大小:8
// 虚表指针vpt_sonClass2 4
// chSC2[3] 4
(2)虚拟继承sonClass1的大小:24
// 因为是虚拟继承,所以有个虚拟指针vtp_sonClass2_sonClass1,大小为4
// sonClass1的大小为20
(3)虚拟继承VirtualInheritance2的大小:8
// 一个虚拟指针ptr_sonClass2_VirtualInheritance2=ptr_sonClass2_sonClass1+偏移量,该指针和ptr_sonClass2_sonClass1共用一个指针,只是偏移量不同,所以大小为0。
即使再多继承几个virtual class,这个指针只算一次。
// VirtualInheritance2的大小为8,
注:
(1)单个类的大小;空类大小为1,成员变量影响类的大小,虚函数有一个虚指针。
(2)普通单继承:[虚指针大小(如果父类中有虚函数)] + 父类大小 + 自身成员变量大小
(3)普通多继承:如果几个父类都有虚函数,则子类与第一个父类共用一个虚表指针,其他有几个有虚函数的父类则就有几个虚表指针。
(4)虚拟单继承:[虚表指针大小(若子类有虚函数)] + 自身成员变量大小 + 虚类指针ptr_sonClass_fatherClass大小 + 父类大小
(5)普通、虚拟混合继承:[虚表指针大小(若子类或普通父类有虚函数)] + 自身成员变量大小 + 公有虚拟指针大小 + 虚拟父类大小 + 普通父类成员变量大小(除虚拟指针)
(6)包含虚函数的多重虚拟继承:[虚表指针大小(若子类有虚函数)] + 自身成员变量大小 + 公有虚类指针大小(不管有几个虚拟父类,只加一个) + 所有父类的大小
参考:
http://blog.csdn.net/jhj735412/article/details/7580498
http://blog.csdn.net/hackbuteer1/article/details/7883531
- C++ 继承、虚继承、虚函数类内存分配
- c++继承 虚函数 内存分配
- 【C++】c++单继承、多继承、菱形继承内存布局(虚函数表结构)
- 【C++】c++单继承、多继承、菱形继承内存布局(虚函数表结构)
- C++ 类 内存分布 虚函数 单继承 多继承
- 【C++】虚函数和虚继承的内存分布情况
- Java继承内存分配
- Java继承内存分配
- C++,继承、虚函数解惑!
- C++,继承、虚函数解惑!
- 虚拟继承与虚函数继承的内存布局
- C++ 多继承和虚继承的内存布局
- JAVA 继承 父类子类 内存分配
- 类(继承)的内存分配详解
- JAVA 继承 父类子类 内存分配
- JAVA 继承 父类子类 内存分配
- JAVA 继承 父类子类 内存分配
- C++ 虚继承 虚函数 内存分布
- centos环境下如何配置yum源
- sql monitor 实践
- Java将图片处理成背景透明的圆形图片
- Linux下vim的常用命令
- first_report_gu
- C++ 继承、虚继承、虚函数类内存分配
- tomcat+myeclipse+mysql环境搭建
- 深入理解数据结构之链表
- 【NOIP2017普及组正式赛】跳房子
- 初识Hibernate(一)之配置文件以及框架搭建
- CSS background背景知识详解
- 【spring框架】spring获取webapplicationcontext,applicationcontext几种方法详解
- c++学习笔记第一篇 列车调度问题
- 制作游戏2