2010-12-09 51 views
48

請看下面的例子:學習C++:多態性與切片

#include <iostream> 
using namespace std; 

class Animal 
{ 
public: 
    virtual void makeSound() {cout << "rawr" << endl;} 
}; 

class Dog : public Animal 
{ 
public: 
    virtual void makeSound() {cout << "bark" << endl;} 
}; 

int main() 
{ 
    Animal animal; 
    animal.makeSound(); 

    Dog dog; 
    dog.makeSound(); 

    Animal badDog = Dog(); 
    badDog.makeSound(); 

    Animal* goodDog = new Dog(); 
    goodDog->makeSound(); 
} 

輸出是:

rawr 
bark 
rawr 
bark 

但我認爲肯定是輸出應該是 「RAWR樹皮樹皮樹皮」。 badDog有什麼用?


更新:你可能會感興趣於another question of mine

+1

我認爲你的變量有一些命名錯誤。 – aioobe 2010-12-09 22:24:18

+2

代碼甚至沒有按原樣編譯。和`void main()`??? Ew ... – 2010-12-09 22:24:25

+2

我不明白爲什麼有人會低估這個問題 - 它既不「不清楚」也不「有用」。 +1來否定downvote。 – casablanca 2010-12-09 22:27:59

回答

64

這是一個稱爲「切片」的問題。

Dog()創建一個Dog對象。如果您打電話給Dog().makeSound(),它會打印出您所期望的「樹皮」。

問題是,您正在使用此Dog初始化badDog,它是Animal類型的對象。由於Animal只能包含Animal而不是從Animal派生的任何東西,因此需要Animal部分的Dog並用此初始化本身。

badDog的類型總是Animal;它永遠不可能是別的。您可以在C++中獲得多態行爲的唯一方法是使用指針(如您已經用您的goodDog示例演示)或使用引用。

的引用(例如,Animal&)可以指從Animal衍生的任何類型和指針的對象(例如,Animal*)可以指向從Animal衍生的任何類型的對象。但是,普通的Animal始終是Animal,沒有別的。

像Java和C#的一些語言已經引用語義,其中變量(在大多數情況下)只是對象的引用,因此給予Animal rex;rex實際上只是一些Animal參考,並rex = new Dog()使得rex指新Dog對象。

C++不能這樣工作:變量不引用C++中的對象,變量是對象。如果您在C++中使用rex = Dog(),則會將新對象Dog複製到rex中,並且由於rex實際上是Animal類型,因此會將其切片並僅複製Animal部件。這些被稱爲值語義,這是C++中的默認值。如果你想在C++中引用語義,你需要明確地使用引用或指針(它們都不是C#或Java中的引用,但它們更類似)。

7
Animal badDog = Dog(); 
    ad.makeSound(); 

當實例化一個Dog和按值指定爲一個Animal變量,則slice的對象。這基本上意味着你從badDog中刪除所有的Dog,並將其設置爲基類。

爲了在基類中使用多態性,您必須使用指針或引用

-1

您使用賦值運算符初始化badDog。因此Dog()被複製爲Animal。程序的輸出是正確的。 :)