![](/img/trans.png)
[英]How can a class member be left unconstructed for later construction with placement new
[英]Delay true base class construction with placement new
我問以下方法是否(以及為什么)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
)?
注意:這是一個后續問題, 如何在C ++ 03中偽造構造函數繼承? 。
編輯 :發布問題時我一定是分心了。 當然,而不是delete b
我的意思是b->~Base()
。 我責怪低血糖!
代碼不正確並觸發未定義的行為。 您的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
而不是Derived
對象(在這種情況下) vtable
s,指向RunTime Type信息的vptr
將引用Base
,而不是Derived
)
可能還有兩三種其他可能出錯的東西,我現在想不到......
delete b
不只是調用Base
的析構函數,它還釋放new
返回的內存並調用Derived
s析構函數[假設您希望Base
有一個虛擬析構函數...如果您打算將它作為非虛擬析構函數,則行為很簡單未定義開頭]。 這意味着您隨后使用placement new會在內存中構建一個不再有效的全新Base
對象,並且您永遠不會替換之前銷毀的Derived
部分。 簡而言之,您所做的一切甚至都不會接近正確的行為。
坦率地說,我沒有看到你想要做什么...為什么Derived
必須是默認構造的,而不僅僅是轉發參數? 它不是愚蠢的,它是事情的方式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.