2011-12-14 56 views
1

功率考慮以下示例代碼:使用的虛函數

class Base { 
public: 
    void f(); 
    virtual void vf(); 
}; 

class Derived : public Base { 
public: 
    void f(); 
    void vf(); 
}; 

#include <iostream> 
using namespace std; 

void Base::f() { 
    cout << "Base f()" << endl; 
} 

void Base::vf() { 
    cout << "Base vf()" << endl; 
} 

void Derived::f() { 
    cout << "Derived f()" << endl; 
} 

void Derived::vf() { 
    cout << "Derived vf()" << endl; 
} 

int main() 
{ 
    Base b1; 
    Derived d1; 
    b1.f(); 
    b1.vf(); 
    d1.f(); 
    d1.vf(); 

    Derived d2;  // Derived object 
    Base* bp = &d2; // Base pointer to Derived object 
    bp->f(); // Base f() 
    bp->vf(); // which vf()? 

    return 0; 
} 

運行的輸出是:

Base f() 

Base vf() 

Derived f() 

Derived vf() 

Base f() 

Derived vf() 

問題:

  1. 在線路Base* bp = &d2,對象類型在編譯時已知。那麼關於bp->vf();使用哪個函數的決定也可以在編譯時進行?

  2. 由於在編譯時本身已知對象類型,因此本示例程序中使用的虛函數的功能是什麼?

+0

直至編譯器。在理性化方面似乎沒有什麼意義。 – 2011-12-14 16:01:59

+0

@ TomalakGeret'kal:那麼在這個例子中,虛擬函數的強大功能不是正確的嗎? – 2011-12-14 16:04:33

+1

這個問題並沒有意義。該語言準確地規定了你得到的行爲*,這正是你所看到的。有什麼不清楚的?更好的問題是「動態調度的成本」。 – 2011-12-14 16:07:59

回答

3

這個程序是微不足道的,並且確實沒有證明虛函數(或者更一般的多態性)的能力。考慮這種變化:

// add second derived class 
class Derived2 : public Base { 
public: 
    void vf() { std::cout << "Derived2 vf()" << std::endl; } 
}; 

// in main 
int user_choice; 
std::cin >> user_choice; 
Base * ptr; 
if (user_choice == 0) 
    ptr = new Derived(); 
else 
    ptr = new Derived2(); 
ptr->vf(); 

這裏,類的選擇取決於用戶輸入 - 編譯器有沒有辦法預測什麼類型的對象ptr實際上會指向到達調用ptr->vf();時。

4

在線路Base* bp = &d2,該對象類型是在編譯時已知的。那麼關於bp->vf();使用哪個函數的決定也可以在編譯時進行?

是的。
這是大多數現代智能編譯器能夠完成的編譯器優化的一部分。

如果編譯器可以確定在編譯時本身調用哪個函數,那麼它會這樣做。儘管這完全取決於編譯器是否可以在編譯時或運行時檢測到確切的函數調用,但虛擬主義可以保證您的程序所需的行爲。

由於在編譯時本身已知對象類型,因此在本示例程序中使用的虛擬函數的功能是什麼?

完全取決於編譯器。大多數現代編譯器都可以在編譯時評估這個函數調用。

1

呃......對於這兩個問題都是和否:這取決於你所爭論的抽象層次。

  • 從語言的角度來看,1)是一種誤解。 d2的類型是已知的,但是當它將d2地址分配給bp時,將會發生從Derived*Base*的轉換。 從這裏開始,靜態類型的bp是Base*(因爲這是它的聲明)以及它指向其Derived的動態類型(因爲這是與對象關聯的運行時類型信息引用的)。 從他們開始,通過bp的每個操作都將Base *視爲一種類型,並且需要爲每個虛擬函數重定向。

  • 通過編譯器的立場,可以完成某些優化,並且由於在所有函數中,pb總是指向Derived,虛擬重定向可以 - 事實上被跳過。 但這是由於您的特定示例的結構,而不是因爲語言功能。

1

In the line Base* bp = &d2, the object type is known at compile time. Then the decision of which function to use in the case of bp->vf(); can also be made at compile time right?

否,到要使用哪個函數的決定是動態在運行時基於所述對象的類型,而不是指針/類型引用該對象來完成。這被稱爲動態綁定。編譯器會保存一個名爲virtual pointervptr的隱藏指針,它指向一個名爲virutal表的表。每個類將有一個具有至少一個虛擬函數的虛擬表(不管爲該類創建多少個對象)。虛擬表包含該類的虛擬功能的地址。

Since the object type is known at compile time itself, is the power of virtual functions used in this sample program?

逸岸指向的對象可能不是在編譯時是已知的。採取這需要基類指針作爲參數的方法的一個例子,如下所示:

void draw(Shape *bp) 
{ 
    bp->draw(); 
} 

在這種情況下,實際的對象可能是從Shape衍生的任何形狀。但繪製的形狀取決於傳入的對象的實際類型。