尊重原创,转载自:这里
C++类的大小,是一个比较经典的问题,学过C++后,应该对类大小有清晰的认识,长话短说,本文精简凝练,我们进入正题!!!
1.类的大小与什么有关系?
与类大小有关的因素:普通成员变量,虚函数,继承(单一继承,多重继承,重复继承,虚拟继承)
与类大小无关的因素:静态成员变量,静态成员函数及普通成员函数
2.空类
空类即什么都没有的类,按上面的说法,照理说大小应该是0,但是,空类的大小为1,因为空类可以实例化,实例化必然在内存中占有一个位置,因此,编译器为其优化为一个字节大小。
某类继承自空类:
class base
{
};
class derived:public base
{
private:
int a;
}
此时,derived类的大小为4,derived类的大小是自身int成员变量的大小,至于为什么没有加上父类base的大小1是因为空白基优化的问题,在空基类被继承后,子类会优化掉基类的1字节的大小,节省了空间大小,提高了运行效率。
3.一般类的大小(注意内存对齐)
首先上两个类的示例:
class base1
{
private:
char a;
int b;
double c;
};
class base2
{
private:
char a;
double b;
int c;
};
虽然上述两个类成员变量都是一个char,一个int,一个double,但是不同的声明顺序,会导致不同的内存构造模型,对于base1,base2,其成员排列是酱紫的:
base1:
base2:
base 1类对象的大小为16字节,而base 2类对象的大小为24字节,因为不同的声明顺序,居然造成了8字节的空间差距,因此,我们将来在自己声明类时,一定要注意到内存对齐问题,优化类的对象空间分布。
4.含虚函数的单一继承
首先呈上示意类:(64位,指针大小8字节)
class Base
{
private:
char a;
public:
virtual void f();
virtual void g();
};
class Derived:public Base
{
private:
int b;
public:
void f();
};
class Derived1:public Base
{
private:
double b;
public:
void g();
virtual void h();
};
基类Base中含有一个char型成员变量,以及两个虚函数,此时Base类的内存布局如下:
内存布局的最一开始是vfptr(virtual function ptr)即虚函数表指针(只要含虚函数,一定有虚函数表指针,而且该指针一定位于类内存模型最前端),接下来是Base类的成员变量,按照在类里的声明顺序排列,当然啦,还是要像上面一样注意内存对齐原则!
继承类Derived继承了基类,重写了Base中的虚函数f(),还添加了自己的成员变量,即int型的b,这时,Derived的类内存模型如下:
此种情况下,最一开始的还是虚函数表指针,只不过,在Derived类中被重写的虚函数f()在对应的虚函数表项的Base::f()已经被替换为Derived::f(),接下来是基类的成员变量char a,紧接着是继承类的成员变量int b,按照其基类变量声明顺序与继承类变量声明顺序进行排列,并注意内存对齐问题。
继承类Derived1继承了基类,重写了Base中的虚函数g(),还添加了自己的成员变量(即double型的b)与自己的虚函数(virtual h() ),这时,Derived1的类内存模型如下:
此种情况下,Derived1类一开始仍然是虚函数表指针,只是在Derived1类中被重写的虚函数g()在对应的虚函数表项的Base::g()已经被替换为Derived1::g(),新添加的虚函数virtual h()位于虚函数表项的后面,紧跟着基类中最后声明的虚函数表项后,接下来仍然是基类的成员变量,紧接着是继承类的成员变量。
5 含虚函数的多重继承
首先上示意类:
```cpp
class Base1
{
private:
char a;
public:
virtual void f();
virtual void g1();
};
class Base2
{
private:
int b;
public:
virtual void f();
virtual void g2();
};
class Base3
{
private:
double c;
public:
virtual void f();
virtual void g3();
};
class Derived:public Base1, public Base2, public Base3
{
private:
double d;
public:
void f();
virtual void derived_func();
}
首先继承类多重继承了三个基类,此外继承类重写了三个基类中都有的虚函数virtual f(),还添加了自己特有的虚函数derived_func(),那么,新的继承类内存布局究竟是什么样子的呢?请看下图!先来看3个基类的内存布局:
紧接着是继承类Derived的内存布局:
首先,Derived类自己的虚函数表指针与其声明继承顺序的第一个基类Base1的虚函数表指针合并,此外,若Derived类重写了基类中同名的虚函数,则在三个虚函数表的对应项都应该予以修改,Derived中新添加的虚函数位于第一个虚函数表项后面,Derived中新添加的成员变量位于类的最后面,按其声明顺序与内存对齐原则进行排列。