C++对象创建时的内存分配

Memory allocation on C++ object creation

本文关键字:内存 分配 对象 创建 C++      更新时间:2023-10-16

在下面的C类上创建对象期间,可以观察到不同大小的内存分配

class C {
 int i;
 int j;
};
void f() {
 C *c = new C;
 C *c2 = new C[2];
 C (*c3)[2] = new C[2][2];
}

c被分配有8个字节;

c2被分配有8*2+4个字节;

c3被分配有8*2*2+4个字节。

为什么c2和c3需要4个字节?

请记住,C++将内存分配对象表达式分离。默认数组新表达式T * p = new T[N];N对象分配足够的内存,构造这些对象。在另一端,delete[] p;必须调用所有这些元素的析构函数,然后释放内存。

虽然分配和释放内存是由平台处理的,并且可释放内存可以通过单个指针值很好地识别给操作系统,但构建和销毁对象更为复杂。实际对象的数量必须存储在某个地方,为此,标准允许C++实现请求比N * sizeof(T)更多的内存。的确,指针p将始终指向N对象数组的开头,但p不必是底层内存分配器返回的同一指针。(事实上,p保证精确地是底层分配结果的值,偏移了多余的内存量。)

细节完全由实施决定。不过,一些平台提供了额外的保障;例如,安腾ABI(它将额外的数据称为"数组cookie")表示new T[N]的内存布局如下:

+- alignof(T) --+-- sizeof(T) --+-- sizeof(T) --+-- sizeof(T) --+-- ...
| ***** [8B: N] |  1st element  |  2nd element  |  3rd element  |  .....
+---------------+---------------+---------------+---------------+-- ...
A               A
|               |_ result of "new T[N]"
|
|_ value returned "operator new[]()"

来自标准([expr.mew]节):

新表达式将请求的空间量作为类型std::size_t的第一个参数传递给分配函数。该论点应不小于正在创建的对象的大小;只有当对象是数组时,它才可能大于正在创建的对象的大小。

显然,该标准期望一些附加信息与动态分配的数组一起存储。

现在,使用调试器来查看这些额外字节中填充了什么,并确定编译器何时可能需要这些额外信息来完成其工作。

(提示:修复程序,使其不会泄漏内存)

许多编译器使用new[]返回的指针前的4个字节来存储实际分配的对象数。这完全取决于实现,重要的是要记住,指针算术会使您超出分配的内存范围,从而导致未定义的行为