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