2015-07-11 62 views
1
#include <iostream> 

using namespace std; 

class dissection { 
    int x; 
    public: 
    void test() { 
    cout<<"Test base"; 
    } 
    void caller(){ test(); } 
}; 

class dissectionDerived: public dissection { 
    int x; 
    public: 
    void test(){ 
    cout<< "Test derived"; 
    } 
}; 

int main(int argc, char ** argv) { 
    dissectionDerived derived; 
    derived.caller(); 
    return 0; 
} 

在上面的代碼示例中,輸出是「測試基地」。我想到的方式是,由於派生函數沒有函數調用者它可以調用基類函數,但由於對象的實際類型是dissectionDerived它將能夠調用測試函數解剖派生類。這是因爲重載解析在基類範圍內找到最接近的測試函數後就停止了嗎?當通過使用派生對象的另一個基本函數調用基本和派生調用基時的常用函數?

如果調用者函數可以從派生函數調用,爲什麼它不能成爲派生類中重載解析的一部分?

我已經使用-cg克++編譯器標誌,然後做了objectfile的OBJ轉儲,輸出如下所示:

SYMBOL TABLE: 

0000000000000000 l d .text._ZN10dissection4testEv 0000000000000000 .text._ZN10dissection4testEv 
0000000000000000 l d .text._ZN10dissection6callerEv 0000000000000000 .text._ZN10dissection6callerEv 
0000000000000000 w F .text._ZN10dissection4testEv 000000000000001d _ZN10dissection4testEv 
0000000000000000   *UND* 0000000000000000 __gxx_personality_v0 
0000000000000000   *UND* 0000000000000000 _ZSt4cout 
0000000000000000   *UND* 0000000000000000 _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc 
0000000000000000 w F .text._ZN10dissection6callerEv 000000000000001a _ZN10dissection6callerEv 

C++ FILT _ZN10dissection4testEv給出解剖::測試()

謝謝!

回答

0

要打印「測試導出」,聲明dissection::test()virtual

virtual void test(){ cout<<"Test base"; } 
+0

如果你爲此解釋了爲什麼是 – JustSid

+0

,那麼我將upvote它是'test()'必須是虛擬的,而不是'caller()'。 – juanchopanza

+0

謝謝,juanchopanza。我一定失去了理智。 – Oswald

2

當你想「這實際的類是參與」,以確定哪些函數被調用,該功能需要被打上virtual

這使得編譯器將「虛擬函數表」作爲該派生類和任何派生類的一部分。

換句話說,使virtual void test();basevirtual屬性將「繼承」,因此您的派生類也將爲virtual - 對於「安全性」,您可以將其標記爲override(例如void test() override { ... },以確保編譯器在嘗試「覆蓋」不是基類的一部分。

1

由於歷史的原因在C默認調度++是邏輯即依賴於靜態類型的參考/指針,而不是實際的對象的類型。

C++中的「靜態類型」意味着引用或指針的類型......即const BaseObj& x;靜態類型x的pe是BaseObj

「動態類型」是指對象實例beging指向或引用的實際類型,可以與靜態類型相同,也可以與其派生的類型相同。例如在BaseObj *p = new DerivedObj;中,靜態和動態類型是不同的。

使用非虛擬方法(不幸是默認方法),名爲的代碼取決於靜態類型,而不取決於實例的實際類型。

這種邏輯錯誤默認的官方原因是效率,因此調用的地址可以在鏈接時計算並且是固定的。

要根據對象類型進行調度,需要聲明成員函數爲virtual。 請注意,virtual關鍵字是每個方法所必需的,但僅在基類中是必需的,因爲在這種情況下,它也假定在所有派生類中......無論如何,它是IMO良好的文檔,可以在派生類中重複它。

還要記住,在C++中,爲需要多態派生和銷燬的類聲明析構函數是非常重要的,因爲即使對於它,否則如果刪除派生實例會遇到麻煩(未定義行爲)在指向基地的指針上使用delete

通常在一個C++程序中,一個類不是被派生出來的(然後virtual關鍵字不存在,你可以從每個實例中刪除幾個字節),或者它是用來派生的,最好是聲明析構函數和其他方法是virtual。在想要派生的類中使用非虛擬方法是一種例外,IMO應該只是爲了證明正確的理由纔會發生。

In the words of C++ original author默認情況下的非虛擬調度僅僅是因爲類不打算用作基礎,並且由於內存優化和字節級與C和Fortran結構的兼容性。

+1

爲什麼偏見?它本身不是「好的」或「錯誤的」。對於你想要的,這只是一個不同的事情。 –

+0

@EmilioGaravaglia:錯在於它是默認的...從語言的角度來看,出於性能原因而進行「非虛擬」調度會更好。 – 6502

+0

可能會受到限制,但我完全無法看到任何概念上的差異。 –

相關問題