为啥要内存对齐
- 性能问题
- CPU一次读一个字,且每次读取都是直接从字的整数倍开始读
- 如果对齐则能保证长度在一个字以内的内存能被一次读完,否则就可能要两次 + 截断,

- 跨平台支持
- 部分硬件架构就是不支持内存不对齐的读取
class / struct内存对齐
默认对齐规则:
- 定义「有效对齐值」= min ( #pragma pack(n)的n(或者通过其他方式设置的手动对齐值), 结构体/类中最宽成员的字节数 ),#pragma pack(n)的n默认为系统字长(32位则4,64位则8)
- 结构体首地址 可以整除 有效对齐值
- 每个成员的首地址与结构体地址的offset 可以整除 min ( 有效对齐值, 自己的字节数 )(这也是为什么建议从大到小、内存对齐的元素优先声明)
- 结构体大小 可以整除 有效对齐值
如果想手动控制对齐规则,可以使用下面的几个方法:
#pragma pack- 设置的手动对齐值是2^n字节 (n整,0 ≤ n ≤ 4)
// 手动指定对齐,强制1字节对齐(禁用填充) #pragma pack(push, 1) class NetworkPacket { uint8_t header; // 0 B uint32_t data; // 1~4 B,不加对齐应该是4~7 B }; // 恢复默认对齐 #pragma pack(pop)
alignas(C++11)
alignas(32) class AlignedData { // C++11 起支持,32字节对齐 float values[8]; // 用于 AVX 指令 };
__attribute__((aligned(n)))(GCC/Clang的属性语法)
- 这个规定结构体起始地址和结构体大小必须被n整除,与有效对齐值有区别
typedef struct mystruct1 { //in64_64 in32_32(可以通过#pragma pack(4)实现) int a; //(4 + 4) 4 double b;// 8 8 char c; //(1 + 15) (1 + 3) }__attribute__((aligned(16))) AMS1;//in 64_64:32; in 32_32:16
class / struct对象模型总体原则
先看个大概:

- 按照字段声明顺序分配内存(注意任意非虚方法是存在.text段的)
- 存在继承的:vfptr和字段内存从祖先基类 → 自己依次分配(虚继承除外,虚基类的实例会被存在末尾)
- 有虚方法的:对象内存的首地址是一个指向虚方法表的vfptr
- 存在多重继承:
- 第一个虚方法表:第一个基类 | 自己独有的
- 然后跟第一个基类的字段
- 之后存其他基类的虚方法表指针。其他虚方法表存的就是其它基类独有的虚方法
- 若多个基类都实现了同一虚方法,则多张虚方法表中都会有这个函数
- 存在虚继承:
- 第一个虚方法表:不装虚基类的虚方法,只装自己独有的
- 紧接其后存储的是有关虚基类的虚方法表的vbptr:实际上存储的是 虚基方法表指针 与 vbptr所在位置 的偏移
- 虚基类的vfptr和字段单独存储在末尾
- 注:如果派生类没有自己独有的虚方法,则开头
- 嵌套后,规则也进行嵌套即可。比如单继承 + 多继承、钻石继承
深度为2的单+多继承:


钻石继承 + 虚继承


Reference: