2009-09-10 45 views
20

我認爲如果foo在類D中聲明,但沒有標記爲虛擬,則以下代碼將調用D中的foo的實現(與d的動態類型無關)。在C++中,如果覆蓋虛擬函數,它是一個自動虛擬的函數嗎?

D& d = ...; 
d.foo(); 

但是,在下面的程序中,情況並非如此。任何人都可以解釋嗎?如果一個方法覆蓋了一個虛擬函數,該方法是否會自動虛擬化?

#include <iostream> 

using namespace std; 

class C { 
public: 
     virtual void foo() { cout << "C" << endl; } 
}; 

class D : public C { 
public: 
     void foo() { cout << "D" << endl; } 
}; 

class E : public D { 
public: 
     void foo() { cout << "E" << endl; } 
}; 

int main(int argc, char **argv) 
{ 
     E& e = *new E; 
     D& d = *static_cast<D*>(&e); 
     d.foo(); 
     return 0; 
} 

上述程序的輸出是:

E 
+4

的的static_cast是多餘 - 'd&d = *的static_cast (&e);'相當於'd&d = E;'由於從E */E&到d *的隱式轉換/ D&。 –

+0

在函數聲明中加入「override」使得它清除你的意圖來覆蓋基類函數,它也會觸發編譯器的錯誤,以防你聲明的函數在const和base之間不同如果你從std :: exception中派生出來並且聲明瞭what()非const) – Ghita

回答

22

標準10.3.2(class.virtual)表示:

如果虛擬成員函數VF在類Base和在派生的類聲明,直接或間接地從基地成員函數VF衍生與Base :: vf具有相同的名稱和參數列表,則Derived :: vf也是虛擬的(不論它是否如此聲明),並且它覆蓋*

[腳註:具有相同名稱的函數但是作爲虛擬函數的不同參數列表(子句結束)不一定是虛擬的並且不會被覆蓋。在重寫函數的聲明中使用虛擬說明符是合法的,但是是冗餘的(具有空的語義)。在確定重寫時不考慮訪問控制(子句class.access)。 ---端foonote]

17

快速答案可能是否定的,但正確答案是

C++不知道的功能隱藏,所以覆蓋沒有虛擬關鍵字的虛擬功能也標誌虛擬功能。

+11

@Yossarian,這就是爲什麼在派生類中聲明虛擬函數爲虛擬以便意圖明確的好習慣。Scott Meyers's在他的Effective C++書中討論了這個問題。 –

+0

我聽說某個地方沒有在派生的方法中標記虛擬會使它們幾乎最終。這使得三階導數成爲非虛擬覆蓋。但是我發現塔德烏斯的標準摘錄由於「間接」這個詞而證明這個信念是錯誤的,不是嗎? –

+2

@ v.oddou「我聽說過某個地方」,人們「聽到某處」的東西通常是虛假的。 ;-)只要在層次結構中具有標記爲'virtual' _somewhere_的相同簽名的函數,那麼具有相同簽名的派生函數就是覆蓋。 '聲明'這個聲明是否包含'虛擬'是不相關的,同樣,關鍵字在其基本外觀之後具有空的語義。 –

0

您並未創建e的對象的任何副本並將其放入d中。所以d.foo()遵循正常的多態行爲並調用派生類方法。在基類中聲明爲虛擬的方法也會在派生類中自動虛擬。

-1

輸出(「E」)的行爲與預期其行爲完全相同。

原因: 該引用的動態(即運行時)類型是E.您正在對D進行靜態向上轉換,但這並不會改變對象的實際類型。

這就是虛擬方法和動態調度背後的想法:在這種情況下,您會看到您正在實例化的類型的行爲,即E。