1

I'm playing new operator overloading recently. I noticed a strange behavior when I overload new[] operator (the new operator for allocating arrays).

Here is my code:

#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;
}

And the result (with gcc 64bit on OS X) is like:

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

I noticed three things: 1st, I asked to allocate 10 objects of 4 bytes in new[], but the actual request received by the function is 48 bytes. 2nd, apparently the first 8 bytes are used for other purpose: the actual address received by ptr2 is 8 bytes after the address returned by the new[] operator. 3rd, the address is also automatically translated (by going forward 8 bytes) in the overloaded delete[] function.

I also noticed that this behavior happens only when I explicitly implement the destructor. If I only use the default destructor, the 8 bytes are just gone.

Can anyone tell me what is happening behind this? What are the 8 bytes used for?

Thanks.

4

1 回答 1

3

允许 array-new 表达式调用 array-operator-new 的空间比数组所需的空间多。所需要的只是array-new 表达式的是指向数组中第一个元素的指针。

实际上,需要额外的空间来存储有关在销毁数组时需要销毁多少元素的信息(有时称为“数组cookie”)。

有趣的是,从 array-operator-new 函数请求的实际额外内存量是完全不可知的,并且可能会随着每次调用而改变。这基本上使array-placement-new 表达式有缺陷且无法使用

仅供参考,相关子句为C++11 5.3.4/10:

new-expression将请求的空间量作为 type 的第一个参数传递给分配函数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).

这里xy是表示数组分配开销的非负未指定值;new-expression 的结果将从 . 返回的值偏移这个量operator new[]。这种开销可以应用于所有数组new-expressions,包括那些引用库函数operator new[](std::size_t, void*)和其他放置分配函数的数组。开销的数量可能因一次调用 new 到另一次而异。


您可能很高兴得知Itanium ABI对数组 cookie 有非常明智的规则;例如,对于容易破坏的对象数组,不需要任何东西。

于 2013-10-13T22:34:45.867 回答