解析 C++ 类内存分布,需要了解以下几个概念:
- 对象的内存分布
- 成员变量的内存分布
- 内存对齐原则
对象的内存分布
一个 C++ 对象在内存中的分布包含三个部分:
- 对象头
- 成员变量
- 对象尾(可选)
对象头包含一些元信息,例如虚表指针等内容。成员变量是对象的核心数据,占用了对象内存的大部分空间。对象尾是一些特殊情况下将会占用的空间,例如空类或虚继承。
成员变量的内存分布
成员变量的内存分布基于内存对齐原则,通常会将变量按照某一最小对齐值对齐,以提高内存读写效率。这个最小对齐值通常是类型的大小,或者由编译器根据硬件平台和编译选项自动生成。
ifdef _MSC_VER
pragma pack(push,1)
endif
class MyData {
public:
int a; // 4 bytes
char b; // 1 byte
double c; // 8 bytes
private:
short d; // 2 bytes
};
ifdef _MSC_VER
pragma pack(pop)
endif
上述代码定义了一个 MyData 类,其中定义了四个成员变量 int a、char b、double c 和 short d,通过 #pragma pack 控制了内存对齐。
不同编译器和平台下的内存对齐有很大的区别,需要使用特定的工具来分析。可以通过 sizeof 运算符来查看一个数据类型占用的字节数。
示例
以下是一个代码示例:
ifdef _MSC_VER
pragma pack(push,1)
endif
class Person {
public:
virtual void speak() { std::cout << "I am a person." << std::endl; }
int age;
char name[20];
double height;
private:
short weight;
};
ifdef _MSC_VER
pragma pack(pop)
endif
在这个例子中,虚继承和普通成员变量的内存排列方式是不同的,可以编写一个小程序来验证:
include
include
int main() {
Person p;
p.age = 20;
strncpy_s(p.name, "Tom", sizeof(p.name));
p.height = 1.78;
std::cout << std::hex
<< reinterpret_cast
<< reinterpret_cast
<< reinterpret_cast
<< reinterpret_cast
<< reinterpret_cast
<< std::dec;
return 0;
}
输出的结果如下:
0028A1F0
0028A1F4
0028A1F8
0028A20C
0028A214
从输出中可以看到,虚继承和成员变量之间存在一定的间隔,而成员变量之间是按照内存对齐原则交错排列的。
另一个示例:
class Animal {
public:
virtual void move() { std::cout << "I am moving." << std::endl; }
};
class Dog : public Animal {
public:
virtual void move() { std::cout << "I am running." << std::endl; }
void bark() { std::cout << "Woof!" << std::endl; }
private:
int age;
};
在这个例子中,Dog 类继承了 Animal 类,且重载了其中的虚函数 move。Dog 类中还有一个非虚函数 bark。
执行以下代码:
Dog d;
std::cout << sizeof(Animal) << std::endl;
std::cout << sizeof(Dog) << std::endl;
输出的结果是:
4
8
从结果可以看到,Animal 类仅包含一个虚函数指针,占用 4 个字节。而Dog 类包含了 Animal 类和一个 int 型成员变量,占用 8 个字节。此外通过查看 Dog 对象中虚表指针的值,可以发现在 Dog 对象中默认存在一个虚表。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:解析C++类内存分布 - Python技术站