2014-01-11 38 views
0

下面是在C++中的虛擬函數的也許是最簡單的例子:C++虛擬方法僅適用於基類指針

#include <iostream> 

class A { 
public: 
    virtual void f() { 
     std::cout << "A"; 
    } 
}; 

class B : public A { 
public: 
    void f() { 
     std::cout << "B"; 
    } 
}; 


int main() { 
    { 
     // calls f() in derived class 
     A* a = new B(); 
     a->f(); 
    } 
    { 
     // calls f() in base class 
     A a = B(); 
     a.f(); 
    } 
} 

該程序的輸出是BA。我預計它是BB,即在任何情況下調用基類。 爲什麼在這裏使用基類指針會有所作爲? 我沒有在標準中找到解釋。

+0

我相信這是比切片不同的問題。在這裏,它似乎是對繼承,多態和運行時調度的誤解。他也錯過了可以同時使用'基類指針'和'基類引用'的事實。 (關於這個話題可能還有另一個SO問題)。 – jww

回答

6

這叫做slicingA a = B();創建一個類型爲A的副本。關於其來源B的所有信息都被遺忘了。利用多態性的唯一方法是通過引用或指針(或允許編譯時多態的機制,例如模板或函數重載)。

3

多態性適用於指針,因爲在指針(或引用)的情況下,您可以區分指針的類型和對象的類型。現在在下面的代碼中:

A a = B(); 
a.f(); 

發生了什麼叫切片。即對象B()被切割並且其基A()被分配給a。

不要忘記讓析構函數變爲虛擬!

+0

感謝AraK。最後,明白了。 – user3186142

0

中的A a = B()的情況下函數調用的順序是這樣的:B級

  1. 默認的構造函數被調用。
    • 類型B的臨時對象在堆棧上創建。
  2. 從B到A的轉換運算符被調用。
    • 類型A的臨時對象在堆棧上創建。
  3. 類A的拷貝構造函數以前一個對象的地址作爲參數被調用。
    • 類型A的非臨時對象在堆棧上創建。
    • 該對象將保留在堆棧中,直到函數返回。
    • 它是A類型的一個對象,因此您可以打印出「A」而不是「B」。

總結這一點,這裏是上面一行的「完整版」:A(B()->operator A())