2013-08-27 91 views
1

假設我有此示例代碼:返回由重載操作新不同的類指針在類

class A 
{ 
public: 
    static void* operator new(size_t sz); 

private: 
int xA; 
float yA; 
}; 

class B : public A 
{ 
private: 
int xB; 
float yB; 
}; 

void* A::operator new(size_t sz) 
{ 
    void* ptr = (void*)new B(); 
    return ptr; 
} 

int main() 
{ 
B* b = (B*) new A(); 
// Use b .. 
delete b; 
return 0; 
} 

在這裏,構造將在該順序被調用(在VS2012測試):

  • 一個構造
  • 乙構造
  • 一個構造

前兩個構造函數調用是因爲重載的operator new函數中的new B()。 但是,然後A函數將被函數返回的指針再次調用,因爲重載的操作符new應該返回一個指向空閒內存的指針(不創建對象),所以再次調用構造函數。

如果我在本例中使用指針b,這是未定義的行爲嗎?

+3

你應該返回原始記憶。新操作符和新表達式(它也會調用ctor)之間有區別 – PlasmaHH

+0

爲'A'重載'new'和'delete'操作符創建/銷燬'B'對象始終是可怕的周圍! –

+0

這裏有些不對勁。發佈後,代碼具有無限遞歸。 'B'從'A'繼承'operator new',因此'A :: operator new'中的'new B()'表達式調用'A :: operator new'。 –

回答

1

您發佈的代碼具有無限遞歸,因爲你叫由A::operator newA::operator new;類BA繼承operator new

除此之外,你對編譯器說謊,導致未定義的 行爲。在new A之後,您有一個指向 類型爲A的對象的指針。您可以合法地將其地址轉換爲B*,但 只需將B*轉換爲A*即可; 其他的東西都是未定義的行爲。

目前還不清楚A::operator newnew B的設置。編譯器會將從operator new返回的任何內存 視爲原始內存;在這種情況下,它 將在其中構建一個A對象,並且從此開始,所有 對象都是A。任何嘗試將它用作B 對象都是未定義的行爲。 (當然,如果你確實需要 ,並毀壞BA::operator new創建的,你不能 因爲你已經覆蓋它

最後:你沒有申報operator newstatic; 它implicily。是,和它的慣用不寫在 的static這種情況下,同樣的,分配的new B到 一個void*結果時,轉換爲地道,它是地道不 ,清楚。(它也最好避免C風格 強制轉換,因爲它們隱藏了太多錯誤。)

+0

感謝您的詳細解答。我不想使用這段代碼,只是想知道這是否是未定義的行爲。 – denidare

1

一般而言,operator new()不應該創建一個對象,它應該爲一個對象創建空間。您的代碼將使用A對象覆蓋B對象,然後將其用作B對象,並且是的,這將是「未定義的」(可能在文檔中的「將對象轉換爲其他類型的對象」最初是作爲)創建。

這可能會出現在這種特殊情況下工作,但如果B構造比較複雜(例如有虛函數B),它會馬上無法正常工作。

如果你想分配一個物體的內存,你可以這樣做:L

void* A::operator new(size_t sz) 
{ 
    void* ptr = (void*)::new unsigned char[sz]; 
    return ptr; 
} 

現在你不會爲同一個對象調用兩個不同的構造函數!

+0

只是一個黑客,但儘管他聲稱,他的代碼從來沒有爲任何東西調用任何構造函數;它在進入第一個構造函數調用之前進入無限遞歸。 –

+0

嗯,我認爲它可能會做,因爲缺乏::在新的(我補充說)前。 –

+0

這是一個很好的預防措施。雖然在你的情況下,不是必須的,因爲'operator new'在被構造的對象的範圍內被查找(或者在new []'的情況下被元素類型),而不在調用的範圍內發生。 (當然,當我想要原始內存的時候,我會直接調用':: operator new(sz)',我似乎更加明確地表達了它的意圖,在這種情況下,你需要'::',因爲這個只是一個日常的函數調用。) –

1

operator new的合同僅僅是內存分配,初始化以後由新的表達完成(通過調用構造函數)或程序代碼,如果你直接調用操作。

你試圖做的事情不能完成,不是那樣的。你可以重新設計使用工廠成員函數會返回一個指向一個B對象,例如...