2011-09-19 53 views
1

C++型變化C++類型的變化 - 虛函數問題刪除期間

我讀過,當你構建一個派生類型,類型的變化,這取決於構造函數被調用。因此,如果您創建派生對象並使用基指針調用虛函數,那麼通常它會映射到派生類中的實現。如果您在基類構造函數中調用虛函數,它將使用基類實現,因爲該對象的類型在技術上與該函數中的基類類型相同。例如(臨時代碼,對不起,如果它不編譯):

class Base { 
    Base() 
    { 
     std::cerr << "Base Constructor."; 
     func(); 
    } 

    virtual void func() { 
     std::cerr << "Func base called." << std::endl; 
    } 
}; 

class Derived : public Base { 
    Derived() 
    { 
     std::cerr << "Derived Constructor."; 
     func(); 
    } 

    void func() { 
     std::cerr << "Func derived called." << std::endl; 
    } 
}; 

int main() { 
    Derived* d = new Derived; 
    delete d; 
} 

應該輸出:

Base Constructor. 
Func base called. 
Derived Constructor. 
Func derived called. 

首先,這是總是正確的還是取決於執行?

如果我使用了RTTI和typeinfo,打印在底座上的類型實際上是底座的類型,還是更多的是不成文的規則類型?

從構造函數中調用虛擬函數是否有危險,或者只要知道自己在做什麼,它是否安全?

回答

8

爲了保持短期和簡單的,你可以有一個規則:

虛機制在構造函數和析構函數

禁止在基類中的虛函數調用總是會調用基類版本的函數,派生類中的相同結果是調用函數的派生類版本。

首先,這是否始終如此?還是與實施有關?

是的,這是永遠正確的。這不是實現相關的。

如果我使用了RTTI和typeinfo,那麼打印在底座上的類型實際上是底座的類型嗎?

是的,它會是基地;當您處於Base類構造函數中時,派生對象甚至不存在。

想到這一點,從構造函數中調用虛函數是危險的,或者只要知道自己在做什麼,它是否安全?

不,從構造函數調用虛函數並不危險,只要你理解它後面的語義。


This C++ FAQ應該是你一個很好看的。

+0

「爲了簡化簡單」和錯誤的。 –

2

它是明確的。

[n3290: 12.7/4]:成員函數,包括虛擬函數 (10.3),可構造或破壞(12.6.2)中被調用。 當虛擬函數是從 構造或從析構函數直接或間接地稱爲,包括建築或 破壞類的非靜態數據成員的過程中,將對象到 該呼叫應用的是在建的對象(稱爲x)或 銷燬,被調用的函數是 構造函數或析構函數類中的最終覆蓋,而不是一個覆蓋更多派生類的 類。如果虛擬函數調用使用顯式的 類成員訪問權限(5.2.5)並且對象表達式引用x的完整對象或該對象的基類子對象之一,但使用 而不是x或其基類子對象之一,行爲是不確定的。

1

有一篇來自Scott Meyers的優秀文章。它來自他的Effective C++書。 該文章可以在: Never Call Virtual Functions during Construction or Destruction

它還討論了一種替代實現。

最近我有一個類似的問題,我解決了這個辦法:

class EthernetFrame 
{ 
protected: 
    /** ctor to be called from derived classes */ 
    EthernetFrame(unsigned inPayloadLength) 
    { 
    calculatePadBytes(inPayloadLength); 
    } 

private: 
    /** calculates needed required PadBytes for Frames < 64B 
    * @param inPayloadLength we need to know the length of the actual L3 frame 
    */ 
    void calculatePadBytes(unsigned inPayloadLength); 

}; 

class IPv4Frame : public EthernetFrame 
{ 
public: 
    /** create empty IPv4 packet */ 
    IPv4Frame() : 
    EthernetFrame(cIPv4_MINIMUM_LENGTH) 
    {}; 
    // IPv4 header + trailer in bytes 
    unsigned cIPv4_MINIMUM_LENGTH; 
protected: 
    /** ctor to be called from derived classes */ 
    IPv4Frame(unsigned inPayloadLength) : 
    EthernetFrame(cIPv4_MINIMUM_LENGTH+inPayloadLength) 
    {}; 

}; 
+0

我不明白你的片段與多態函數調用的相關性。 –

+0

代碼片段不涉及關於函數的多態性。這是一個從鏈接文章派生出來的實現,適用於我自己的問題。 –

+0

其實它很貼切,這就是爲什麼我爲它+1了。我在閱讀了那本書的那部分內容之後問了這個問題(它是什麼,第20項?) - 我只是想進一步問一些後續問題。他的片段是Myers提出的解決方案的一個很好的應用。無論如何,我讚賞真實世界的樣本:) –