一、空类
1.真空类
class CNull
{
};
主要是因为空类还是要被实例化的,为了保证每个实例在内存中都有独一无二的地址。编译器就给空类隐含的增加了一个字节。
2.空类
class CNull2
{
public:
CNull2(){printf("Construct/n");}
~CNull2(){printf("Desctruct/n");}
void Foo(){printf("Foo/n");}
};
这种情况和空类是差不多的,内部成员函数并不会影响类的大小
二、继承
1.简单类
class COneMember
{
public:
COneMember(int iValue = 0){m_iOne = iValue;};
private:
int m_iOne;
};
因为有int型成员变量
2.简单继承
class CTwoMember:public COneMember
{
private:
int m_iTwo;
};
子类成员接在父类成员之后的。就相当于有两个int型的整形变量。
3.再继承
class CThreemember:public CTwoMember
{
public:
CThreemember(int iValue=10) {m_iThree = iValue;};
private:
int m_iThree;
};
孙类成员接在子类之后,再再继承就依次类推
4.多重继承
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;
};
总结
- 普通单继承,只需将自身成员变量的大小加上父类大小。如果父类中有虚函数,子类中就不管有没有虚函数都不用加该大小;如果父类中没有虚函数,若子类中有虚函数的话需要加上指向虚表的指针大小。
- 普通的多继承,若几个父类都有虚表,则子类与第一个父类共用一个虚表指针
三、虚继承
1.虚继承
class CTwoMember:virtual public COneMember
{
private:
int m_iTwo;
};
主要的就是因为存在虚函数指针,其指向了一个虚函数表存放了三部分的信息,包括了RTTI运行时类型信息、虚函数指针相对于整体作用域的偏移还有虚函数的入口地址。
E8 2F 42 00 //指针,指向一个关于偏移量的数组,且称之虚基类偏移量表指针
CC CC CC CC // m_iTwo
00 00 00 00 // m_iOne(虚基类数据成员)
2.带虚函数的空类
class CVirtualNull
{
public:
CVirtualNull(){printf("Construct/n");}
~CVirtualNull(){printf("Desctruct/n");}
virtual void Foo(){printf("Foo/n");}
};
因为虚函数里面会存在一个虚函数指针指向一个虚函数表。但是请记住,虚函数表只有一张,当子类中又存在一个虚函数的时候,不会因为增加了新的虚函数而多出来一张表,新的虚函数的指针将添加在复制了的虚表后面。