【C++学习】c++类大小计算
【C++学习】c++类大小计算
涉及到c++中求类大小时需要特别注意一下几点
1.为类的非静态成员数据的类型大小之和。
2.有编译器额外加入的成员变量的大小,用来支持语言的某些特性(如:指向虚函数的指针、虚继承、多重继承)。
3.为了优化存取效率,进行的边缘调整。
4. 与类中的构造函数,析构函数以及其他的成员函数无关。
5. 私有继承,会去继承之前的私有成员变量么?
会……在内存中仍然分配相应的空间,只是在子类中是不可见的。
6. 在做多层次的继承类大小时某个子类的类大小总是等于父类的大小加上子类中数据成员和是否有虚函数,是否是虚继承等因素来决定。
空类大小
首先:我们要知道什么是类的实例化,所谓类的实例化就是在内存中分配一块地址.
那我们先看看一个例子:
#include <iostream>
using namespace std;
class BaseA
{
public:
BaseA();
~BaseA();
private:
};
BaseA::BaseA()
{
}
BaseA::~BaseA()
{
}
class BaseB
{
public:
BaseB();
~BaseB();
private:
};
BaseB::BaseB()
{
}
BaseB::~BaseB()
{
}
class DeriveC : public BaseA
{
public:
DeriveC();
~DeriveC();
virtual void fun(){
cout << "Do something here !" << endl;
}
private:
};
DeriveC::DeriveC()
{
}
DeriveC::~DeriveC()
{
}
class DeriveD : public BaseB , public DeriveC
{
public:
DeriveD();
~DeriveD();
private:
};
DeriveD::DeriveD()
{
}
DeriveD::~DeriveD()
{
}
int main(){
cout << "sizeof( BaseA) : " << sizeof( BaseA) << endl;
cout << "sizeof( BaseB) : " << sizeof( BaseB) << endl;
cout << "sizeof(DeriveC) : " << sizeof( DeriveC) << endl;
cout << "sizeof(DeriveD) : " << sizeof( DeriveD) << endl;
return 0;
}
输出结果如下:
上面是在VS2012编译的结果。
为什么会出现这种结果呢?初学者肯定会很烦恼是吗?类BaseA,BaseB明明是空类,它的大小应该为为0,为什么 编译器输出的结果为1呢?这就是我们刚才所说的实例化的原因(空类同样可以被实例化),每个实例在内存中都有一个独一无二的地址,为了达到这个目的,编译器往往会给一个空类隐含的加一个字节,这样空类在实例化后在内存得到了独一无二的地址。所以BaseA,BaseB的大小为1。
虚函数
如下示例代码:
#include<iostream>
using namespace std;
class Base
{
public:
virtual void aa(){}
private:
char k[3];
};
class Derive: public Base
{
public:
virtual void bb(){}
};
int main()
{
cout<<"Base's size is "<<sizeof(Base)<<endl;
cout<<"Derive's size is "<<sizeof(Derive)<<endl;
return 0;
}
运行结果如下:
说明:有虚函数的类有个virtual table(虚函数表),里面包含了类的所有虚函数,类中有个virtual table pointers,通常成为vptr指向这个virtual table,占用4个字节的大小。成员类Derive public继承于Base,类Derive的虚函数表里实际上有两个虚函数Base::aa()和Derive::bb(),类Derive的大小等于char k[3]的大小加上一个指向虚函数表指针vptr的大小,考虑内存对齐为8。
虚继承
如下示例代码:
#include<iostream>
using namespace std;
class Base
{
public:
virtual void aa(){}
private:
char k[3];
};
class DeriveA: public Base
{
public:
virtual void bb(){}
};
class DeriveB:virtual public Base
{
public:
virtual void cc(){}
};
int main()
{
cout<<"Base's size is "<<sizeof(Base)<<endl;
cout<<"DeriveA's size is "<<sizeof(DeriveA)<<endl;
cout<<"DeriveB's size is "<<sizeof(DeriveB)<<endl;
return 0;
}
VS2012上运行结果如下:
GCC编译之后运行结果如下:
说明:类DeriveB里包含,继承的char k[3],继承的虚函数,类DeriveB的虚函数表里有A::aa(),因为是虚继承,还有一个指向父类的指针,该指针为指向虚基类的指针(Pointer to virtual base class)。考虑内存对齐,总大小为12,不知道为啥VS2012里面会多出4个字节出来,对虚继承的作用以及原理,后面我会单独写一篇文章来记录。
多重继承
如下示例代码:
#include<iostream>
using namespace std;
class BaseA
{
public:
virtual void aa(){}
private:
char k[3];
};
class BaseB
{
public:
virtual void bb(){}
private:
char q[3];
};
class Derive: public BaseA , public BaseB
{
public:
virtual void cc(){}
private:
char j[3];
};
int main()
{
cout<<"BaseA's size is "<<sizeof(BaseA)<<endl;
cout<<"BaseB's size is "<<sizeof(BaseB)<<endl;
cout<<"Derive's size is "<<sizeof(Derive)<<endl;
return 0;
}
VS2012编译之后运行结果如下:
像这种继承方式,在类Derive中会维护两个虚函数指针,第一个指向第一个基类的虚函数表(并且带上在类Derive中定义的虚函数),第二个指针指向第二个基类(BaseB)的虚函数表…其他的类似。
综合例子:
#include <iostream>
using namespace std;
class A
{
char k[3];
public:
virtual void f(){};
};
class B : public virtual A
{
char i[3];
public:
virtual void f1(){};
};
class C: public virtual A{
//virtual void f(){};
char j[3];
public:
virtual void t(){};
};
class D: public B,public C
{
char g[3];
public:
virtual void s();
};
int main()
{
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
cout<<sizeof(C)<<endl;
cout<<sizeof(D)<<endl;
return 0;
}
VS2012上编译之后运行结果:
GCC编译之后运行结果如下:
最主要的是sizeof(D): 4个char数组,对齐后是16,又是多重继承,那么有两个虚函数表的指针,又需要维护一分A类的指针,那么是16+4*2+4=28…采用虚继承,目的就是为了解决二义性和减小内存开销,所以在D中只维护一份A的指针便可。只有VS2012编译之后运行结果为啥是这样,目前还不是很清楚。