2011-11-21 74 views
1

考慮下面的代碼和下面的問題:有關操作問題的new()和operator delete()

/* 
* GCC 4.4 
*/ 
#include <iostream> 

using namespace std; 

class A { 
public: 

    void* operator new(size_t s) { 
     cout << "A::operator new(size_t) called\n"; 
    } 

    void operator delete(void* p) { 
     cout << "A::operator delete(void*) called\n"; 
    } 

    void* operator new(size_t s, A* p) { 
     cout << "A::operator new(size_t, A*) called\n"; 
    } 

    void operator delete(void* p, size_t s) { 
     cout << "A::operator delete(void*, size_t) called\n"; 
    } 

}; 

void* operator new(size_t s) { 
    cout << "::operator new(size_t) called\n"; 
} 

void operator delete(void* p) { 
    cout << "::operator delete(void*) called\n"; 
} 

void* operator new(size_t s, A* p) { 
    cout << "::operator new(size_t, A*) called\n"; 
} 

void operator delete(void* p, size_t s) { 
    cout << "::operator delete(void*, size_t) called\n"; 
} 

int main() {  
    A* p1 = new A(); // See question 1. 
    delete p1; // See question 2. 
    A* p2 = new (p1) A(); // See question 3. 
    delete p2; // See question 4. 
} 

下面的問題看似多餘莫名其妙。但是,我試圖區分的是C++標準規則中由實現定義的內容所定義的內容。

  1. operator new(size_t)在任何情況下使用(從A或從全局命名空間 拍攝,無論是默認或沒有)。還行吧。 現在嘗試,只除去void* A::operator new(size_t) {}:爲什麼編譯器爲:

    error: no matching function for call to ‘A::operator new(unsigned int)’ note: candidates are: static void* A::operator new(size_t, A*)

    的粗野,編譯器從全局命名空間 拿起::operator new(size_t)

  2. 爲什麼operator delete(void*)優選operator delete(void*, size_t) (當這些版本都存在於其中operator delete (void*)取自同一命名空間)?

  3. 如果我刪除void* A::operator new(size_t, A*), ,但爲什麼代碼不能編譯,儘管在全局命名空間中定義了相同版本的運算符?

    error: no matching function for call to ‘A::operator new(unsigned int, A*&)’ note: candidates are: static void* A::operator new(size_t)

  4. 爲什麼編譯器還是喜歡 operator delete (void*),雖然A::operator new(size_t, A*)已使用 爲獲得P2?

+0

這是很多問題。如果你想親自看看[n3242.pdf](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/),我會從'5.3開始.4新「(第110頁) – sehe

+0

無論如何,我認爲所有的問題都與同一主題有關。 – Martin

+4

如果有可能解決整個問題的單個答案,Q/A格式的效果最好。當幾個答案結合起來提供答案時,你會對接受答案的困惑感到困惑。這使得信息難以/不可能爲未來的SO搜索者找到。 – sehe

回答

2

讓我們想像一些情節。首先,以下總是作品:

A * p1 = ::new A; 
::delete p1; 

A * p2 = ::new (addr) A;  // assume "void * addr" is valid 
p2->~A(); 

全球表達式總是使用相應的運營商從全局命名空間,所以我們很好。請注意,沒有「放置 - 刪除」表達式。通過調用析構函數,每個展示位置構造對象必須顯式銷燬

接下來,假設我們寫:

A * p3 = new A; 
delete p3; 

這一次,分配函數operator new(size_t)A的範圍第一次擡起頭來。該名稱存在,但是如果刪除了正確的重載,則會出現錯誤。 (這回答Q1和Q3。)operator delete()也是如此。單參數版本((void *))優於雙參數版本((void *, size_t))沒有特別的原因;你應該有兩個之一。 (另請注意,全球版本的雙參數函數)。

最後,讓我們重新訪問placement-new表達式。由於沒有放置 - 刪除表達式,因此您的最後一行代碼是錯誤(未定義行爲):您不得通過deletedelete任何未通過默認值-new表達式獲得的任何內容。如果您定義了一個placement-new分配函數,則還應該定義匹配的釋放函數。這個函數只會在一個特定的情況下自動調用,但是:如果一個放置 - 新表達式new (a, b, c) Foo;導致構造函數拋出異常,則然後將調用相應的釋放函數。否則,由於所有佈局構建都是手動的,因此通常只會手動調用佈局釋放函數(並且通常根本不會,因爲它幾乎沒有實際的工作)。

典型的情況可能是這樣的:

void * addr = ::operator new(sizeof(Foo)); // do real work 

Foo * p = new (addr, true, 'a') Foo;  // calls Foo::operator new(void*, bool, char);, 
              // then calls the constructor Foo::Foo() 

// in case of exception, call Foo::operator delete(addr, true, 'a') 

p->~Foo(); 

Foo::operator delete(addr, true, 'a');  // rarely seen in practice, often no purpose 

::operator delete(addr);     // do real work 

要兜了一圈開口代碼示例,請注意標準的要求,全球::operator delete(void *, void *)什麼也不做。也就是說,全球佈局 - 新需要零清理。

0

一個對象不記得它是如何被創建的;只有在相應的展示位置投放時纔會使用展示位置刪除,否則將使用常規刪除操作員。

2

關於你的第一個和第三個問題,它的工作原理IMO這樣的:

  1. 因爲你沒有使用::新,編譯器試圖找到一個新的運營商在A類。
  2. 它找到一個,但它找不到適當的過載,所以它失敗。

如果您明確指出::new,則不應該有任何問題。如果編譯器無法在該類中找到新運算符的專用版本,則只能進入全局名稱空間。

從標準:第5.3.4,

9. If the new-expression begins with a unary :: operator, the allocation function’s name is looked up in the global scope. Otherwise, if the allocated type is a class type T or array thereof, the allocation function’s name is looked up in the scope of T. If this lookup fails to find the name, or if the allocated type is not a class type, the allocation function’s name is looked up in the global scope.

+0

「*如果此查找無法找到名稱,或者分配的類型不是類型,則在全局範圍內查找分配函數的名稱。*」< - Q1和Q3詢問他們爲何獲得錯誤而不是此規則觸發。 – zwol

+0

@Zack:這就是答案。請注意,標準中提到的關於* name *的查詢 - 超負荷的候選人決議是另一回事。可以在那裏找到一些'new'的合法定義(這是g ++打印的),所以規則不會被觸發。如果A類中沒有'new'函數,則可以通過註釋第二個定義來觀察規則。 –