怎么计算C++继承、虚继承、虚函数类的大小?
一、真空类
C++代码
classCNull
{
};
长度:1
内存结构:
评注:长度其实为0,这个字节作为内容没有意义,可能每次都不一样。
二、空类
C++代码
classCNull2
{
public:
CNull2(){printf("Construct/n");}
~CNull2(){printf("Desctruct/n");}
voidFoo(){printf("Foo/n");}
};
长度:1
内存结构:
评注:同真空类差不多,内部的成员函数并不会影响类大小。
三、简单类
C++代码
classCOneMember
{
public:
COneMember(intiValue=0){m_iOne=iValue;};
private:
intm_iOne;
};
长度:4
内存结构:
00 00 00 00 //m_iOne
评注:成员数据才影响类大小。
四、简单继承
C++代码
classCTwoMember:publicCOneMember
{
private:
intm_iTwo;
};
长度:8
内存结构:
00 00 00 00 //m_iOne
CC CC CC CC //m_iTwo
评注:子类成员接在父类成员之后。
五、再继承
C++代码
classCThreemember:publicCTwoMember
{
public:
CThreemember(intiValue=10){m_iThree=iValue;};
private:
intm_iThree;
};
长度:12
内存结构:
00 00 00 00 //m_iOne
CC CC CC CC //m_iTwo
0A 00 00 00 //m_iThree
评注:孙类成员接在子类之后,再再继承就依此类推了。
六、多重继承
C++代码
classClassA
{
public:
ClassA(intiValue=1){m_iA=iValue;};
private:
intm_iA;
};
classClassB
{
public:
ClassB(intiValue=2){m_iB=iValue;};
private:
intm_iB;
};
classClassC
{
public:
ClassC(intiValue=3){m_iC=iValue;};
private:
intm_iC;
};
classCComplex:publicClassA,publicClassB,publicClassC
{
public:
CComplex(intiValue=4){m_iComplex=iValue;};
private:
intm_iComplex;
};
长度:16
内存结构:
01 00 00 00 //A
02 00 00 00 //B
03 00 00 00 //C
04 00 00 00 //Complex
评注:也是父类成员先出现在前边,我想这都足够好理解。
七、复杂一些的继承
不写代码了,怕读者看了眼花,改画图。
长度:32
内存结构:
01 00 00 00 //A
02 00 00 00 //B
03 00 00 00 //C
04 00 00 00 //Complex
00 00 00 00 //OneMember
CC CC CC CC //TwoMember
0A 00 00 00 //ThreeMember
05 00 00 00 //VeryComplex
评注:还是把自己的成员放在最后。
只要没涉及到“虚”(Virtual),我想没什么难点,不巧的是“虚”正是我们要研究的内容。
八、趁热打铁,看“虚继承”
C++代码
classCTwoMember:virtualpublicCOneMember
{
private:
intm_iTwo;
};
长度:12
内存结构:
E8 2F 42 00 //指针,指向一个关于偏移量的数组,且称之虚基类偏移量表指针
CC CC CC CC // m_iTwo
00 00 00 00 // m_iOne(虚基类数据成员)
评注:virtual让长度增加了4,其实是多了一个指针,关于这个指针,确实有些复杂,别的文章有具体分析,这里就不岔开具体讲了,可认为它指向一个关于虚基类偏移量的数组,偏移量是关于虚基类数据成员的偏移量。
九、“闭合”虚继承,看看效果
长度: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只出现了一次,而且是在最后边。当然了,更复杂的情况要比这个难分析得多,但虚继承不是我们研究的重点,我们只需要知道:虚继承利用一个“虚基类偏移量表指针”来使得虚基类即使被重复继承也只会出现一次。
十、看一下关于static成员
C++代码
classCStaticNull
{
public:
CStaticNull(){printf("Construct/n");}
~CStaticNull(){printf("Desctruct/n");}
staticvoidFoo(){printf("Foo/n");}
staticintm_iValue;
};
长度:1
内存结构:(同CNull2)
评注:可见static成员不会占用类的大小,static成员的存在区域为静态区,可认为它们是“全局”的,只是不提供全局的访问而已,这跟C的static其实没什么区别。
十一、带一个虚函数的空类
C++代码
classCVirtualNull
{
public:
CVirtualNull(){printf("Construct/n");}
~CVirtualNull(){printf("Desctruct/n");}
virtualvoidFoo(){printf("Foo/n");}
};
长度:4
内存结构:
00 31 42 00 //指向虚函数表的指针(虚函数表后面简称“虚表”)
00423100:(虚表)
41 10 40 00 //指向虚函数Foo的指针
00401041:
E9 78 02 00 00 E9 C3 03 … //函数Foo的内容(看不懂)
评注:带虚函数的类长度就增加了4,这个4其实就是个指针,指向虚函数表的指针,上面这个例子中虚表只有一个函数指针,值就是“0x00401041”,指向的这个地址就是函数的入口了。
十二、继承带虚函数的类
C++代码
classCVirtualDerived:publicCVirtualNull
{
public:
CVirtualDerived(){m_iVD=0xFF;};
~CVirtualDerived(){};
private:
intm_iVD;
};
长度:8
内存结构:
3C 50 42 00 //虚表指针
FF 00 00 00 //m_iVD
0042503C:(虚表)
23 10 40 00 //指向虚函数Fo