我最近正在玩新的运营商重载。当我重载new []运算符(用于分配数组的new运算符)时,我注意到一种奇怪的行为。
这是我的代码:
#include <iostream>
using namespace std;
class Pool
{
public:
void* alloc(size_t size) {
return malloc(size);
}
};
class MyClass
{
public:
MyClass() {
cout<<"ctor called"<<endl;
}
~MyClass() {
cout<<"dtor called"<<endl;
}
void* operator new(size_t size) {
cout<<"new called, size: "<<size<<endl;
return (void*)malloc(size);
}
void* operator new[](size_t size) {
cout<<"new[] called, size: "<<size<<endl;
void* result = (void*)malloc(size);
cout<<"in new[]: "<<result<<endl;
return result;
}
void* operator new(size_t size, void* ptr) {
cout<<"new(ptr) called, size: "<<size<<endl;
return (void*)ptr;
}
void* operator new(size_t size, Pool& pool) {
cout<<"new(Pool) called, size: "<<size<<endl;
return (void*)pool.alloc(size);
}
void operator delete(void* ptr) {
cout<<"delete called, ptr: "<<ptr<<endl;
free(ptr);
}
void operator delete(void* ptr, size_t size) {
cout<<"delete called, ptr: "<<ptr<<", size: "<<size<<endl;
free(ptr);
}
void operator delete[](void* ptr) {
cout<<"delete[] called, ptr: "<<ptr<<endl;
free(ptr);
}
void operator delete[](void* ptr, size_t size) {
cout<<"delete[] called, ptr: "<<ptr<<", size: "<<size<<endl;
free(ptr);
}
uint32_t data;
};
int main() {
Pool pool;
cout<<"Pool"<<endl;
new Pool;
cout<<"MyClass"<<endl;
MyClass *ptr1, *ptr2, *ptr3;
ptr1 = new MyClass;
ptr2 = new MyClass[10]();
cout<<(void*)ptr2<<endl;
ptr3 = new(pool) MyClass;
delete ptr1;
delete[] ptr2;
delete ptr3;
return 0;
}
结果(在OS X上使用gcc 64bit)就像:
Pool
MyClass
new called, size: 4
ctor called
new[] called, size: 48
in new[]: 0x7fa7f0403840
ctor called
ctor called
ctor called
ctor called
ctor called
ctor called
ctor called
ctor called
ctor called
ctor called
0x7fa7f0403848
new(Pool) called, size: 4
ctor called
dtor called
delete called, ptr: 0x7fa7f0403830
dtor called
dtor called
dtor called
dtor called
dtor called
dtor called
dtor called
dtor called
dtor called
dtor called
delete[] called, ptr: 0x7fa7f0403840
dtor called
delete called, ptr: 0x7fa7f0403870
我注意到三件事:第一,我要求在new []中分配10个4字节的对象,但该函数收到的实际请求是48字节。 2,显然前8个字节用于其他目的:ptr2
接收的实际地址是new []运算符返回的地址后的8个字节。 3,地址也在重载的delete []函数中自动翻译(通过前进8个字节)。
我还注意到只有在我明确实现析构函数时才会发生这种行为。如果我只使用默认的析构函数,那么8个字节就消失了。
谁能告诉我这背后发生了什么? 8个字节用于什么?
感谢。
答案 0 :(得分:3)
允许array-new表达式调用array-operator-new,其空间大于数组所需的空间。所需要的只是数组新表达式的值是指向数组中第一个元素的指针。
实际上,需要额外的空间来存储有关在销毁阵列时需要销毁多少元素的信息(有时称为“数组cookie”)。
值得注意的是,从array-operator-new函数请求的实际额外内存量是完全不可知的,并且可能随着每次调用而改变。这基本上是array-placement-new expression defective and unusable。
仅供参考,相关条款为C ++ 11 5.3.4 / 10:
new-expression 将请求的空间量作为
std::size_t
类型的第一个参数传递给分配函数。该参数不得小于正在创建的对象的大小;仅当对象是数组时,它可能大于正在创建的对象的大小。
最有趣的例子如下:
new T[5]
致电operator new[](sizeof(T) * 5 + x)
,
new(2,f) T[5]
致电operator new[](sizeof(T) * 5 + y, 2, f)
。此处,
x
和y
是非负的未指定值,表示数组分配开销;结果 new-expression将从operator new[]
返回的值中抵消此金额。此开销可应用于所有数组 new-expressions ,包括引用库函数operator new[](std::size_t, void*)
和其他放置分配函数的那些。开销的数量可能因新的一次调用而异。
您可能会高兴地了解到Itanium ABI对阵列Cookie有非常明智的规则;例如,对于易破坏的物体阵列,不需要任何物体。