总结:空类占用1个字节,非空类的大小=非静态成员变量占用大小(静态变量不占内存)+虚函数占用内存(4字节,不管有多少个虚函数)+字节对齐的开销(成员函数不算入类的空间),子类大小=本身成员变量大小+父类大小
来看一个总的例子:
- #include<stdio.h>
- class Car
- {
- public:
- Car(void){};
- ~Car(void){};
- public:
- int a;
- char b;
- char* c;
- static int d;
- void Fun(){};
- void Fun1()
- {
- int a1;
- long a2;
- };
- virtual Fun3(){};
- virtual Fun4(){};
- };
- void main()
- {
- int size = 0;
- Car objCar;
- size = sizeof(objCar);
- printf("%s %d /r", "Class Car Size:", size);
- }
输出结果:16
其中int a占4个字节,char b占一个字节,char * c占4个字节,static int d不占空间,void Fun成员函数不占空间,void Fun1里面定义了两个变量也不占空间,Fun3跟Fun4只占4个字节(指针所占空间,具体见下文分析)。
4+1+3(字节对齐)+4+4=16
以下引用:http://blog.csdn.net/yzx0803060320/article/details/7302130
先看这么个问题——已知:
{
int a;
char * p;
};
那么运行cout<<"sizeof(CBase)="<<sizeof(CBase)<<endl;之后输出什么?
这个应该很简单,两个成员变量所占的大小有嘛——8。可由时候人就是爱犯这个错误:这么简单的问题人家会问你?再想想……好像C++类里面有个什么 函数指针,也应该占字节吧!?什么指针来着?忘了(还是水平低不扎实)!流汗中……算了姑且认为是构造函数和析构函数吧。一人一个加上刚才那8个16个。 好笑吗?这是我犯的错误!!!到底C++类的sizeof是多少呢?没有所谓的函数指针问题吗?不甘心,编个例子看看:
第一步:给丫来个空的(不好意思上火粗鲁了)
{
};
运行cout<<"sizeof(CBase)="<<sizeof(CBase)<<endl;
sizeof(CBase)=1;
为什么空的什么都没有是1呢?查资料……查啊查……OK这里了:先了解一个概念:类的实例化,所谓类的实例化就是在内存中分配一块地址,每个实例在内存中都有独一无二的地址。同样空类也会被实例化(别拿豆包不当干粮,空类也是类啊),所以编译器会给空类隐含的添加一个字节,这样空类实例化之后就有了 独一无二的地址了。所以空类的sizeof为1。继续下一步:
第二步:
还是最初的那个类,运行结果:sizeof(CBase)=8
没什么说的,两个内部变量的大小。难道我记错了没有什么指针问题的存在?再试试(早这么有求知欲也不会丢人了,这回来劲了)
第三步:添个虚函数
{
public :
CBase( void );
virtual ~ CBase(void );
private :
int a;
char * p;
};
再运行:sizeof(CBase)=12
嗨!问题出来了!!跟虚函数有关。为什么呢?查资料ing……
有了:“C++ 类中有虚函数的时候有一个指向虚函数的指针(vptr),在32位系统分配指针大小为4字节”噢原来如此害死我了。那么继承类呢?
第四步:
基类就是上面的了不写了
public CBase
{
public :
CChild( void );
~ CChild(void );
private :
int b;
};
运行:cout<<"sizeof(CChild)="<<sizeof(CChild)<<endl;
输出:sizeof(CChild)=16;
可见子类的大小是本身成员变量的大小加上父类的大小。
关于虚拟继承(相当于添加了一个接口):
class COneMember
{
public:
COneMember(int iValue = 0){m_iOne = iValue;};
private:
int m_iOne;
};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(虚基类数据成员) |
关于闭合继承:
class ClassA
{
public:
ClassA(int iValue=1){m_iA = iValue;};
private:
int m_iA;
};
class ClassB:public ClassA
{
public:
ClassB(int iValue=2){m_iB = iValue;};
private:
int m_iB;
};
class ClassC: public ClassC
{
public:
ClassC(int iValue=3){m_iC = iValue;};
private:
int m_iC;
};
class CComplex :public ClassB, public ClassC
{
public:
CComplex(int iValue=4){m_iComplex = iValue;};
private:
int m_iComplex;
};
长度: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成员
class CStaticNull { public: CStaticNull(){printf("Construct\n");} ~CStaticNull(){printf("Desctruct\n");} static void Foo(){printf("Foo\n");} static int m_iValue; }; |
长度:1
内存结构:(同CNull2 )
评注:可见static 成员不会占用类的大小,static成员的存在区域为静态区,可认为它们是“全局”的,只是不提供全局的访问而已,这跟C 的static 其实没什么区别。
带有虚函数的类:
class CVirtualNull
{
public:
CVirtualNull(){printf("Construct\n");}
~CVirtualNull(){printf("Desctruct\n");}
virtual void Foo(){printf("Foo\n");} //或者是virtual void Foo()=0;都是占4个字节大小
};
长度:4
内存结构:
00 31 42 00 //指向虚函数表的指针(虚函数表后面简称“虚表”)
00423100:(虚表) 41 10 40 00 //指向虚函数Foo 的指针
00401041: E9 78 02 00 00 E9 C3 03… // 函数Foo 的内容(看不懂) |
子类有新的虚函数:
class CVirtualDerived: public CVirtualNull { public: CVirtualDerived(){m_iVD=0xFF;}; ~CVirtualDerived(){}; virtual void Foo2(){printf("Foo2\n");}; private: int m_iVD; }; |
长度:8
内存结构:
24 61 42 00 //虚表指针 FF 00 00 00 //m_iVD
00426124:(虚表) 23 10 40 00 50 10 40 00 |
评注:虚表还是只有一张,不会因为增加了新的虚函数而多出另一张来,新的虚函数的指针将添加在复制了的虚表的后面。
注意:当空类多继承空类时,一个空类占一个字节,一个空类继承一个空类还是占一个字节,一个空类继承两个空类时还占一个字节;一个空类继承N(N>2)个空类时,占N-1个字节。不知道为什么,谁知道告诉我