C++类的大小
类本身是没有大小的。类的大小,指的是类的对象所占的大小。如果用sizeof运算符对一个类型名操作,得到的是具有该类型实体的大小。
类型 | 32位编译器 | 64位编译器 |
---|---|---|
char | 1个字节 | 1个字节 |
char*(即指针变量) | 4个字节 | 8个字节 |
short int | 2个字节 | 2个字节 |
int | 4个字节 | 4个字节 |
unsigned int | 4个字节 | 4个字节 |
float | 4个字节 | 4个字节 |
double | 8个字节 | 8个字节 |
long | 4个字节 | 8个字节 |
long long | 8个字节 | 8个字节 |
unsigned long | 4个字节 | 8个字节 |
影响类大小的因素:
1. 普通成员变量
2. 虚函数:因为虚函数表指针带来的影响
3. 继承(单一继承,多重继承,重复继承,虚拟继承):虚继承对类的大小有影响,是因为虚基表指针带来的影响
4. 字节对齐
不影响类大小的因素:
1. 静态成员变量
2. 静态成员函数
3. 普通成员函数
//64位系统
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class A {
A();
~A();
public:
virtual void fun(); //大小:8
private:
int a; // 成员变量1,大小:4
char *b; // 成员变量2,大小:8
float c; // 成员变量3,大小:4
double d; // 成员变量4,大小:8
long e; // 成员变量5,大小:8
char f; // 成员变量6,大小:1
std::vector<std::string> g; // 成员变量7,大小:24, 取决于vector类的大小
};
class B : virtual public A
{
B();
~B();
private:
char a;
};
class C : virtual public A, virtual public B
{
C();
~C();
private:
int a;
};
int main()
{
cout << "类A的大小: " << sizeof(A) << endl;
cout << "类B的大小: " << sizeof(B) << endl;
cout << "类C的大小: " << sizeof(C) << endl;
return 0;
}
//结果
//只有成员变量1和2,4+8=12,但是由于需要字节对齐,所以大小为8+8=16
//只有成员变量6和7,1+24=25,但是由于需要字节对齐,所以大小为8+24=32
//只有成员变量1和6,4+1=5,但是由于需要字节对齐,所以大小为4+4=8
//只有虚函数成员变量1和6,8+4+1=13,但是由于需要字节对齐,所以大小为8+4+4=16
字体对齐原则:
对于结构体类型与类对象的对齐原则:使用成员当中最大的对齐字节来对齐。(前提是不超过8字节,当超过8字节,则采用8字节对齐)
指定对齐字节值: 意思是指使用了宏 #pragma pack(n)来指定的对齐值
虚函数:
类的大小为8。含有多个虚函数,类的大小依然为8,原因:
虚函数是通过虚函数表实现的,虚函数表实际上是一个函数指针数组,它保存了本类中的虚函数的地址。虚函数表属于类中而不属于类的某个实例,所以不会为每个实例专门生成一个虚函数表,但每个类的实例中保存指向了这个虚函数表的指针(所以包含虚函数的对象的大小会增加一个指针的大小),而且这个指针保存在对象实例空间的最前面。
继承
1. 单一继承/多重继承:子类的大小为自己的和所继承的类的成员按对齐方式结合
2. 重复继承:不会计算重复的
3. 虚拟继承:在多重继承中,如果发生了如:类B继承类A,类C继承类A,类D同时继承了类B和类C。最终在类D中就有了两份类A的成员,这在程序中是不能容忍的。当然解决这个问题的方法就是利用虚继承。
空类的大小为1:
原因是为了让对象的实例能够相互区别。空类同样能够被实例化,并且每个实例在内存中都有独一无二的地址,因此,编译器会给空类隐含加一个字节,这样空类实例化之后就会拥有独一无二的内存地址。
如果没有这一个字节的占位,那么空类就无所谓实例化了,因为实例化的过程就是在内存中分配一块地址。
当空类作为另一个类的成员时,大小计算在内。但是当一个类继承空类时:大小不计算,空白基类最优化(EBO/EBCO)