我正在实现一个堆栈分配器并为某个类重载operator new[]
以使用我自己的分配器。然后我注意到operator new[]
为分配的数组中的元素数量分配了内存
例如:
test_class* test_arr = new test_class[5];
从我的分配器请求8个字节+5 * sizeof(test_class),在前8个字节中它存储数组的大小,在这种情况下为5。
为什么这样做?我的分配器的工作是跟踪分配的内存量。对于那部分它真的没有意义,是吗?那有什么意义呢? 此外,我可以(或不应该吗?)我以某种方式“关闭”?
答案 0 :(得分:2)
最有可能new T[N]
将请求指针p
到大小为sizeof(size_t) + N * sizeof(T)
字节的内存,将N存储在第一个sizeof(size_t)
字节中,然后返回(T*)(((size_t*)p)+1)
用户(因此第一个T
位于返回的指针处。)
然后delete[]
将获取给定的指针,查看*(p-sizeof(size_t))
以查看要销毁的对象数量,销毁它们,并将((size_t*)p)-1
传递给要释放的基础分配器。
答案 1 :(得分:1)
当您在代码中编写p = new T[N]
时,编译器会生成调用operator new[]
的代码,以便为类型为N
的{{1}}对象分配足够的内存,以及所需的任何簿记信息。当您随后调用T
时,编译器为delete[] p
指向的数组中的每个N
元素调用析构函数,然后调用p
以释放它所获得的内存来自operator delete[]
。
但operator new[]
并不总是具有相同的价值。你可以做N
,两次删除会分别运行不同数量的析构函数。
为了调用正确数量的析构函数,必须在某处记录p = new T[3]; delete[] p; p = new T[4]; delete[] p;
指向的数组中有多少个对象。该注释通常存储在编译器从调用p
获得的内存的簿记部分中。这就是它需要额外空间的原因。
这是目前的典型实现,但编译器并不需要这样做。例如,至少有一个早期实现保留了一个指针值和析构函数计数的单独表。
此外,许多实现不会为没有析构函数的类型使用额外的开销。因此operator new[]
只需拨打int *p = new int[3]
,operator new(3*sizeof(int)
只需拨打delete[] p
。