2014-04-24 84 views
0

我有以下情形:當異步回調調用虛函數,而基類構造函數尚未返回時會發生什麼?

class Caller{ 
public: 
    Caller()    {...} 
    void register(Base* b) {...} 
    void callBase() { b->virt()} 
}; 

class Base { 
public: 
    Base(Caller c)  { println("Base::Base()"); c.register(this); sleep(30); } 
    virtual void virt() { println("Base::virt()"); } 
}; 

class Derived : public Base { 
public: 
    Derived()   { println("Derived::Derived()"); } 
    virtual void virt() { println("Derived::virt()"); } 
}; 

我通常知道,如果有人呼籲派生類派生::的virt的virt()將被調用。但是在這裏,如果當Base在父構造函數中休眠時調用函數callBase,哪個函數將被調用? Base :: virt()或Derived :: virt()?

謝謝

+0

[C++虛構函數來自構造函數]可能的重複(http://stackoverflow.com/questions/496440/c-virtual-function-from-constructor) – Lilshieste

回答

1

按照C++ Lite FAQ 23.5Base::virt()將被稱爲。

無論如何,這實際上不是你想要做的 - 如果你的對象被另一個線程使用而沒有正確的初始化,你可能會遇到各種討厭的競爭條件。例如,如果第二個線程在vtable設置爲時調用virt,會發生什麼情況?誰來保證在對象構建過程中設置vtable是一個原子操作?

你應該設計你的代碼,使得你的對象在完全初始化之前不被使用。

+0

你是對的。我認爲崩潰的原因是競爭條件,當子構造函數沒有完成時(字段還沒有創建),而回調調用子虛函數並且它試圖使用這些字段。 – user1819676

3

它會叫Base::virt。在基類構造函數完成之前,將虛擬函數調度爲動態類型爲Base

(儘管從技術上說你有不確定的行爲,如果另一個線程訪問沒有正確同步的對象。而程序可能不會編譯,因爲register是關鍵字。)

1

在構造函數或析構函數中Base中,該類幾乎在所有方面都起作用,就像它是Base一樣。
這意味着Base - 使用任何被稱爲虛擬函數的實現。

如果沒有(這是可能的,因爲Base可以聲明爲純虛,使Base一個抽象類),你會得到​​3210,所以一切都可能發生。
這種情況下經常看到的行爲包括崩潰在無效訪問,故意中止與錯誤和調用一些派生類覆蓋。

如果呼叫同時發生(多線程/信號),標準說你直接去UB

Java,C#等在這裏有完全不同的規則,所以要針對每種新語言單獨進行檢查。

另外:在C++中使用register作爲名稱是不明智的。這是不允許的,因爲它是一個不存在的(即根本不起作用)存儲級關鍵字。

相關問題