刚看完了《深度探索C++对象模型》第三章,这里做一下总结,也写一下我自己在g++ 7.5.0上的验证。
本文中所有的源文件都可以在这里拿到(百度网盘链接)。
注意,这里所说的“对象”是指在C++中使用class
或struct
关键字创建的类的实例。
1. 无继承情况下的C++对象内存布局
首先当然是从最基础的情况来讲,在没有继承的情况下的C++对象内存布局是什么样的?这又分为两种:无虚函数和有虚函数。
1.1 无虚函数
C++类内成员变量分为两类:static
成员变量和非static
成员变量。static
成员变量不在类的实例的内部,在整个内存中只有一份,只需要使用类名即可访问;而非static
成员变量在类的实例内部,需要为其分配空间。
在这种情况下C++的对象和C的结构体是一样的,毕竟要实现和C的兼容,主要就是结构体/类内成员变量的对齐。
其一般规则总结如下:
- 所有成员按照在类内的声明顺序在内存中排列;
// test00.cpp
#include <iostream>
int main();
class Test00 {
friend int main();
public:
int i1;
private:
int i2;
public:
int i3;
};
#define showOffset(ClassName, memberName) (reinterpret_cast<unsigned long>( &(static_cast<ClassName*>(nullptr)->memberName)))
int main() {
std::cout << showOffset(Test00, i1) << std::endl;
std::cout << showOffset(Test00, i2) << std::endl;
std::cout << showOffset(Test00, i3) << std::endl;
}
// Output:
// 0
// 4
// 8
- 任一非
static
成员变量的偏移(offset
)要是其大小的倍数;
// test01.cpp
#include <iostream>
struct Test01 {
char c;
int i; // 如果紧凑排列,则i的偏移为1,但i的size为4,偏移要是4的倍数,因此i的偏移为4
};
#define showOffset(ClassName, memberName) (reinterpret_cast<unsigned long>( &(static_cast<ClassName*>(nullptr)->memberName)))
int main() {
std::cout << showOffset(struct Test01, i) << std::endl;
}
// Output:
// 4
- 结构体整体的size需要为最大非
static
成员变量size的倍数;
// test02.cpp
#include <iostream>
// 如果紧凑排,Test02_1的size应为9,
// 但要与int(size为4)对其,所以其size为12
struct Test02_1 {
char c1; // Offset: 0
int i; // Offset: 4
char c2; // Offset: 8
};
// Test02_2成员和Test02_1相同,但顺序不同,
// 受规则2和3影响,其size为8
struct Test02_2 {
char c1; // Offset: 0
char c2; // Offset: 1
int i; // Offset: 4
};
int main() {
std::cout << "sizeof Test02_1: " << sizeof(Test02_1) << std::endl;
std::cout << "sizeof Test02_2: " << sizeof(Test02_2) << std::endl;
}
// Output:
// sizeof Test02_1: 12
// sizeof Test02_2: 8
- 空对象的size为1,为了保证每个对象都有唯一的内存位置(memory location)
// test03.cpp
#include <iostream>
struct Test03 {}; // Empty class
int main() {
Test03 a, b;
std::cout << "sizeof Test03: " << sizeof(Test03) << std::endl;
if (&a == &b)
std::cerr << " Error! &a == &b, at " << static_cast<void*>(&a) << std::endl;
else
std::cout << "a and b has different address, &a = " << static_cast<void*>(&a) << " and &b = " << static_cast<void*>(&b) << std::endl;
}
// Output:
// sizeof Test03: 1
// a and b has different address, &a = 0x7fffe62e8486 and &b = 0x7fffe62e8487
- 当类(
class
)/结构体(struct
) A 作为一个类B的内部成员变量时,其对齐要求为类A内部最大的对齐要求;
// test04.cpp
#include <iostream>
// 规则2中的类,size为8,对齐要求为4
struct Test01{
char c;
int i;
};
struct Test04 {
char c; // Offset: 1, size 1
Test01 t; // Offset: 4, size 8
};
#define showOffset(ClassName, memberName) (reinterpret_cast<unsigned long>( &(static_cast<ClassName*>(nullptr)->memberName)))
int main() {
std::cout << "Offset of t in struct Test04: " << showOffset(Test04, t) << std::endl;
std::cout << "sizeof Test04: " << size