sizeof计算的是变量、指针或者类在栈中占用的字节数。这里重点讲解一下类的大小。
计算方法:
sizeof(类)=成员变量所占字节数(1、2、4、8字节对齐后)+虚函数指针
sizeof(虚继承类)=
本身成员变量所占字节数+
父类的大小(多继承也是一样的,如果多个父类公用一个基类,则去掉重复的基类占的空间)+
指向父类指针的大小+
虚函数指针大小(虚继承条件下:如果子类新定义了新的虚函数,需要重新开一份虚表,则此处添加一个指向虚函数表的指针;
如果覆盖了父类的虚函数,则将这个虚函数也放到父类的虚函数表里面。)
sizeof(普通继承类)=
本身成员变量所占字节数+
父类的大小(多继承也是一样的,如果多个父类公用一个基类,则去掉重复的基类占的空间)+
虚函数指针大小(非虚继承条件下:把虚函数地址放到父类的虚函数表里面。若父类中没有虚函数,则新建一个)
综合上述两方面的继承,得到总的继承类大小计算方法:
sizeof(继承类)=
本身成员变量所占字节数+
父类的大小(多继承也是一样的,如果多个父类公用一个基类,则去掉重复的基类占的空间)+
指向父类指针的大小(若是虚继承父类才有此项)+
虚函数指针大小(虚继承条件下:如果子类新定义了新的虚函数,需要重新开一份虚表,则此处添加一个指向虚函数表的指针;
如果覆盖了父类的虚函数,则将这个虚函数也放到父类的虚函数表里面。非虚继承条件下:把虚函数地址放到父类的虚函数表里面。
)
注:虚函数统一放在虚函数表里管理,然后类中仅保存一份指向虚函数表的指针。上面红色部分是在别人的博文中找到的,但是我使用vs2010发现在虚继承条件下,只要子类中出现虚函数就需要创建一个虚函数指针。
(1)空类
};
运行cout<<"sizeof(A)="<<sizeof(A)<<endl;之后输出1。
先了解一个概念:类的实例化,所谓类的实例化就是在内存中分配一块地址,每个实例在 内存中都有独一无二的地址。同样空类也会被实例化,所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了 独一无二的地址了。所以空类的sizeof为1。
(2)非空类
class B
{
int a;//成员变量占4字节
char *p;//指针占4字节
};
运行cout<<"sizeof(B)="<<sizeof(B)<<endl;之后输出8。
(3)带有虚函数
{
public :
C( void );
virtual ~ C( void );//需要建立虚函数表,类中存储一个大小为4字节的虚函数指针
private :
int a;//4字节
char * p;//4字节
};
sizeof(C)=12
(4)子类
public :
D( void );
~ D( void );
private :
int b;//4字节
};
sizeof(D)=16;//4+12(父类的大小)
(5)普通继承子类有新的虚函数,覆盖
class E
{
public:
E(){printf("Construct\n");}
~E(){printf("Desctruct\n");}
virtual void Foo(){printf("Foo\n");} //或者是virtual void Foo()=0;都是占4个字节大小,建立一个虚函数表,类内保存一个虚函数表指针占4字节
};
class F: public E//非虚继承
{
public:
F(){m_iVD=0xFF;};
~F(){};
virtual void Foo2(){printf("Foo2\n");}; //虚函数直接放在父类的虚函数表内,并没有重新建一个虚函数表。
private:
int m_iVD;//4字节
};
sizeof(F)=8
长度:8 内存结构:
24 61 42 00 //虚表指针 FF 00 00 00 //m_iVD
00426124:(虚表) 23 10 40 00 50 10 40 00 |
class E2: public E//非虚继承
{
public:
E2(){m_iVD=0xFF;};
~F(){};
virtual void Foo(){printf("Foo\n");}; //虚函数直接放在父类的虚函数表内,并没有重新建一个虚函数表。
private:
int m_iVD;//4字节
};
sizeof(E2)=8
(6)虚拟继承
class G
{
public:
G(int iValue = 0){m_iOne = iValue;};
private:
int m_iOne;
};
class H:virtual public G
{
private:
int m_iTwo;
};
长度:12;// 4字节(成员变量)+4字节(指向父类的虚拟指针)+4字节(父类大小)
内存结构:
E8 2F 42 00 //指针,指向一个关于偏移量的数组,且称之虚基类偏移量表指针 CC CC CC CC // m_iTwo 00 00 00 00 // m_iOne(虚基类数据成员) |
(7)虚继承子类中定义了新的虚函数,新建一个虚拟表,在子类中存储一个虚拟表指针
class HH: public virtual G
{
public:
HH(int iValue = 0){m_iOne = iValue;};
virtual void Foo();//建立新的虚表,类内存储虚函数表指针
private:
int m_iTwo;
};
sizeof(HH)=sizeof(成员变量)+sizeof(虚拟表指针)+sizoof(指向父类指针)+sizeof(父类)=4+4+4+4=16。
(8)虚继承子类中覆盖父类的虚函数,覆盖函数地址仍放在父类虚函数表中,在子类中不需要存储一个虚函数表指针
class HHH: public virtual HH
{
public:
HHH(int iValue = 0){m_iOne = iValue;};
virtual void Foo();//需建立新的虚表
};
sizeof(HHH)=sizeof(成员变量)+sizeof(虚拟表指针)+sizoof(指向父类指针)+sizeof(父类)=0+4+4+16=24。
class H2: public virtual HH
{
public:
H2(int iValue = 0){m_iOne = iValue;};
void Foo();//需建立新的虚表
};
sizeof(H2)=24。
sizeof(HHH)=sizeof(成员变量)+sizeof(虚拟表指针)+sizoof(指向父类指针)+sizeof(父类)=0+4+4+16=24。
(9)static 成员
class CStaticNull
{
public:
CStaticNull(){printf("Construct\n");}
~CStaticNull(){printf("Desctruct\n");}
static void Foo(){printf("Foo\n");}
static int m_iValue;
};
长度:1
static成员存储在内存的公共数据区(数据段),不占用栈空间。
(10)多重继承