C++ 继承、虚继承、虚函数类内存分配

目录:
一、单个类的大小
二、普通单继承
三、普通多继承
四、虚拟单继承
五、包含虚函数的普通、虚拟混合继承
六、包含虚函数的多重虚拟继承

一、单个类的大小
(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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值