2011-04-05 65 views
0

我如何知道在編譯時或從任何類的運行時解析函數調用?繼承中的虛擬呼叫

例如 - 在調用show()時從派生類中,它會在運行時解析嗎?

#include <iostream> 
using std::ostream; 

class Base 
{ 
public: 
    virtual void show() { 

     show(); //Call derived class 'show()' 
    } 
}; 

class Derived : public Base { 

public: 

    void show() { 

     show(); //Call to itself, Would this call be resolved at run-time? 
    } 
}; 

ostream& operator <<(ostream &os, Base &obj) 
{ 

    obj.Base::show(); 
    return os; 
} 

int main() 
{ 
    Derived D; 

    cout << D << endl; 
} 
+0

用-S編譯成彙編列表。很容易看出虛擬調度(運行時)和普通函數調用之間的區別。 – 2011-04-05 01:42:04

+0

@Mikael:「虛擬調度...正常函數調用」 - 如果是這些選項,從性能的角度來看真的沒什麼關係:重要的是當它是虛擬調度還是內聯。 – 2011-04-05 02:15:09

+0

我擔心你誤解了虛擬分派:'Base :: show()'不應該調用show()...當虛擬過載可用時,不會調用它,派生類實現是直接調用的。 'Derived :: show()'不應該調用'show()' - 這將無限遞歸,直到堆棧空間耗盡。 – 2011-04-05 02:19:52

回答

2

每當要調用通過混凝土類型的對象(即,沒有指針)的成員函數,靜態類型因此已知的編譯器將解析在編譯時正確的函數。 虛擬函數在運行時解析唯一的時間是當你使用多態性在指向對象的指針上調用它們時。

在你的例子中,通過「this」指針調用show(),然後在運行時解析它們。考慮到實現show()的繼承鏈中可能總是存在一個類。

明確限定的調用「obj.Base :: show()」顯然是在編譯時解析的。

0

只要編譯器能夠確定要調用哪個函數的重載,它就會。它保證能夠做到這一點的時候

  • 它擁有對象的完整類型(例如Foo foo; foo.bar();
  • 你告訴它重載調用(如Foo foo; foo.Bar::bar();

可能能夠在不太明顯的情況下做到這一點 - 也就是說,如果它能夠發現「這個指向Foo的指針總是指向Bar」。這就是所謂的虛擬化,是優化編譯器的夜視鏡的一部分。根據您的編譯器,並根據您的實際代碼,可能會執行此優化 - 並直接調用您的函數而不通過vtable。

+0

在這些情況下,如果想要保證靜態多態性,無論編譯器內部的魔法程度如何,都可以使用CRTP(Curiously Recurring Template Pattern)。 – xDD 2011-04-05 01:59:49

+0

如果你想保證,當然,但是CRTP並不總是現實生活中的一種選擇,並且虛擬化不是你應該指望的東西 - 如果編譯器能夠爲你做到這一點可以是很好的 – rlc 2011-04-05 02:09:00

0

要回答你的問題,在你的代碼中obj.Base :: show()正在編譯時解析,因爲顯式調用了Base函數。如果你想在運行時解決這個問題,那麼你可以使用一個指向Base的指針並傳遞一個指向Derived的指針。

例如:

ostream& operator <<(ostream &os, Base *obj) 
{  
    obj->show(); 
    return os; 
} 

int main() 
{ 
    Derived D;  
    cout << &D << endl; 
} 

我不知道你正在嘗試做的。從你的代碼看來,你想從基類的show()函數中調用派生的(多態的,在運行時版本中解析的)。由於Derived版本是虛擬的,因此不需要這樣做。