2010-01-02 64 views
6

我覺得這很奇怪。在Sample_Base的ctor中,我調用了內部調用fun()的bar(),它是一個純虛函數。我得到了稱爲「純虛函數」的錯誤。這很好。現在,如果我直接從Sample_Base的ctor調用fun(),我不會得到那個錯誤。我在VC++ 2010 Beta 2和Ubuntu 9.10上的g ++ 4.4.1上嘗試過。我同意,除了純虛擬析構函數之外,爲純虛函數提供實現是沒有意義的。但是,我對這種行爲有點驚訝。純虛函數調用error

class Sample_Base 
{ 
public: 
    Sample_Base() 
    { 
     bar(); 
     // fun(); 
    } 
    /* This is code does not throw any error. 
    Sample_Base() 
    { 
     fun(); 
    } 
    */ 

    void bar() 
    { 
     fun(); 
    } 
    virtual void fun() = 0; 
    virtual ~Sample_Base(); 
}; 

Sample_Base::~Sample_Base() 
{ 

} 

void Sample_Base::fun() 
{ 
    std::cout << "Sample_Base::fun\n"; 
} 

class Sample_Derived : public Sample_Base 
{ 
public: 
    Sample_Derived() : Sample_Base() 
    { 
     fun(); 
    } 

    void fun() 
    { 
     std::cout << "Sample_Derived::fun\n"; 
    } 

    ~Sample_Derived() 
    { 

    } 
}; 

回答

6

當您直接調用函數時,由於您在構造函數中,因此編譯器會解析對象的靜態類型(Sample_Base)並直接調用Sample_Base::fun()。既然你爲它提供了一個實現,編譯器找到了這個函數並且它可以工作。

當您間接調用它時,通過bar(),編譯器必須使用動態類型,因此它會執行虛擬調用,以便在運行時解析。在那裏它失敗了,因爲它調用了純虛函數。

所以差異在於它將函數綁定到調用。

+0

[C++標準](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf)§10.4.6指出「虛擬調用純虛函數...正在創建的對象...未定義「。你能評論一下嗎?我會讀它直接調用'fun'也應該是未定義的(即使編譯器在這種特定情況下不會產生崩潰的程序)。或者這不是在這種情況下的虛擬調用(如果是這樣,爲什麼,我無法在標準中找到它)。 – Xlea 2015-04-23 13:13:59

+0

@Xlea Charles Bailey在答案中指出了標準的正確部分。請注意,他當時正在使用C++ 03標準,因此現在章節號會有所不同,但這可能會引導您。 – Gorpik 2015-04-24 07:41:33

1

調用虛函數將不會調用派生類中的重載函數。在構造函數或析構函數中調用純虛函數是未定義的行爲

您可能會感興趣的閱讀thisthis.

1

在施工時間,當Sample_Base構造函數被調用時,對象還沒有完全建立。具體來說,屬於Sample_Derived的部分尚未創建,將被Sample_Derived覆蓋的虛擬功能的調用將不調用Sample_Derived中的實現,而是在Sample_Base中定義的版本。由於該函數沒有實現,所以你會得到一個錯誤。請參閱this entry in the C++ FAQ Lite

+0

我同意,當在Sample_Base中調用bar時,該對象不是完全構造的,但是當我從Sample_Base的構造函數調用fun()時,我不會收到任何錯誤。這就是我所感興趣的。 – Jagannath 2010-01-02 10:29:03

+0

@Jagannath:接收或不接收錯誤不是你應該擔心的,你應該擔心這個設計是危險的。您應該嘗試避免從構造函數/析構函數調用虛擬方法。 – 2010-01-02 10:39:25

+0

@Gal:我知道設計不正確,應該避免從構造函數中調用虛函數。我只是對行爲的差異感興趣。不管怎麼說,多謝拉。 – Jagannath 2010-01-02 10:42:34

4

提供純虛函數的定義並不一定是毫無意義的。標記虛函數純意味着封閉類是抽象的,並且從它派生的任何類都是抽象的,除非該函數的最終覆蓋不是純虛函數。純虛擬函數仍然可以通過顯式的非虛擬調用來調用。

在基類構造體(而不是從一個構造函數-初始化)虛擬函數的版本通過虛擬呼叫稱爲是一個在該類中定義本身或其基礎之一,而不是任何重寫它的類(它不會被構造)。這在12.7 [class.cdtor]/3中明確指定。

在構造函數主體中顯式調用純虛函數是合法的(即使用顯式類限定符) - 雖然這需要函數定義一個主體 - 但它是未定義的行爲來調用純虛函數通過只能從抽象類的構造函數或析構函數中進行的虛擬調用。這在10.4 [class.abstract]/6中明確指定。

1

此行爲不是未定義的,它是明確定義的:虛擬函數在構造函數和析構函數中不是虛擬的。他們稱之爲函數的靜態版本。如果函數是純虛函數,則會導致VC中出現着名的「純虛擬調用」錯誤。

我在一個多線程程序中看到了一個有趣的變體:線程B正試圖調用一個虛函數時,一個對象正在線程A上被破壞。在構造函數或析構函數中沒有虛函數調用,但我們仍然遇到純虛函數調用錯誤。