2013-10-13 51 views
1

最近我正在玩新運算符重載。當我重載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 64位)是這樣的:

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 

我注意到了三兩件事:1,我要求撥出10 new []中有4個字節的對象,但函數收到的實際請求是48個字節。第二,顯然前8個字節用於其他目的:ptr2收到的實際地址是新的[]運算符返回的地址之後的8個字節。 3,地址也在重載的delete []函數中自動翻譯(通過前進8個字節)。

我也注意到,這種行爲只發生在我明確實現析構函數時。如果我只使用默認的析構函數,那麼這8個字節就沒了。

有誰能告訴我這背後發生了什麼?什麼是8字節用於?

謝謝。

回答

3

array-new表達式允許使用比數組所需更多的空間來調用array-operator-new。所有這一切都需要數組新表達式的是指向數組中第一個元素的指針。

實際上,需要額外空間來存儲有關在數組銷燬時有多少元素需要銷燬的信息(有時稱爲「數組cookie」)。

有趣的是,從array-operator-new函數請求的額外內存的實際數量是完全不可知的,並可能隨着每次調用而改變。這基本上使得array-placement-new expression defective and unusable

僅供參考,有關條款是C++ 11 5.3.4/10:

新表達穿過的空間要求的分配的功能類型的第一個參數的量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是表示陣列分配開銷非負未指定的值; 新表達式的結果將由operator new[]返回的值抵消此金額。該開銷可應用於所有陣列新表達式,包括那些引用庫函數operator new[](std::size_t, void*)和其他放置分配函數的那些。開銷的數額可能會隨着新的調用而變化。


你可能會很高興地得知,Itanium ABI大約有陣餅乾非常明智的規則;例如,對於可破壞對象的數組,不需要任何東西。

+0

非常感謝。仍然我很困惑,爲什麼只是添加一個析構函數會需要這樣的開銷。 –

+0

@JimmyJi:你需要知道*有多少個*對象。 –

+0

是的,我明白了。但是,如果我刪除析構函數聲明(僅使用由編譯器生成的缺省聲明),則只需要40個字節。額外的8個字節消失了。在這種情況下,人們將如何知道分配了多少個對象? –

相關問題