2011-11-03 44 views
1

我在問是否(以及爲什麼)以下方法是a)合法和b)道德。我正在強調C++ 03,但也歡迎C++ 11的注意事項。這個想法是爲了防止派生類本身可以默認構造執行愚蠢的B::B(int foo) : A(foo) {}構造函數。延遲真正的基類建設與新的位置

class Base { 
    private: 
    int i; 
    Base(int i) : i(i) {} 
    protected: 
    Base() {} 
    public: 
    static Base* create(int i); 
}; 
class Derived : public Base { 
}; 

Base* Base::create(int i) { 
    Derived* d = new Derived(); 
    Base* b = static_cast<Base*>(d); 
    delete b; 
    new(b) Base(i); 
    return d; 
} 

我的直覺告訴我,這裏有什麼可疑的。如果任何Derived類在其構造函數中訪問Base成員,我想要在其他地方,但否則我無法查看該方法不好的正確原因。

無論如何,如果您認爲這是一個可接受的方法,如何處理參考成員(如int& Base::j)?

注意:這是對How can I fake constructor inheritance in C++03?的後續問題。


編輯:我必須張貼的問題時已經分心。當然,而不是delete b我的意思是b->~Base()。我怪怪低血糖!

回答

3

該代碼不正確,並觸發未定義的行爲。您的Base類沒有虛擬析構函數,這意味着delete b將導致未定義的行爲。

爲UB呼叫從一個事實,即它不會發佈的派生資源(這似乎是代碼的目的,哎喲!),這樣的事實,它會嘗試的理由delete範圍釋放分配的內存,這可能工作與否,取決於兩個對象的佈局。如果它不能釋放內存,它可能會崩潰,如果它成功的放置新的調用在下面的行將嘗試初始化內存中的對象已被釋放...

即使您更改了代碼試圖避免釋放問題)到:

Base* Base::create(int i) { 
    Derived *d = new Derived; 
    Base * b = static_cast<Base*>(d); 
    b->~Base();      // destruct, do not deallocate 
    new (b) Base(i); 
    return d; 
} 

在沒有delete,因此特定的未定義行爲的來源消失了,代碼仍然是不確定的行爲(可能在太多的方法,甚至提)。一旦對析構函數的調用仍然是UB,即使不是,重新創建Base類型的事實意味着該對象的動態分派可能會將該對象視爲對象爲Base而不是對象(在該情況下的vtable S,指向運行時類型信息的vptr將把Base,而不是Derived

而且有可能是兩個或三個其他的東西,可以去錯了,我想不出現在...

+0

如果有,我會打電話給'〜派生',當我說'刪除b'時,我不會。我不是這個意思。 – bitmask

+0

@bitmask:無論你想要什麼都不重要......除非基類具有虛擬析構函數,否則語言不允許你調用指向基類的指針。 –

+0

@bitmask:討論所有這些方法的方法有太多錯誤,但是您必須考慮內存分配/釋放,每個時間點對象的運行時類型...... –

1

delete b不只是調用Base的析構函數,它也釋放內存返回由new編輯,並調用Derived的析構函數[假設你打算有Base有一個虛擬析構函數...如果你打算它是非虛擬的,行爲只是不明確的開始]。這意味着您隨後使用的新貼圖將在內存中構建一個不再有效的全新對象,並且您永遠不會替換之前銷燬的部分Derived。總而言之,你所做的任何事情都無法接近正確的行爲。

坦率地說,我沒有看到你想要做什麼......爲什麼Derived是默認構造的,而不是僅僅轉發一個參數?它不愚蠢,它的方式完成。

+0

是的,我知道你的意思。有時我會嘗試做出更好的事情,最終使事情變得更糟。這是一個反覆出現的模式。 – bitmask