2013-05-19 56 views
1

在下面的代碼,爲什麼的最後通話吃(ç回報「動物b爲食。」?從我的理解,Ç是實例b派生類吃的()的參考是一個虛擬函數。所以它應該返回「狗b正在吃東西。」呼籲基準的參考)虛函數

#include <string> 
#include <iostream> 

using namespace std; 

class Animal 
{ 

protected: 
    string name; 

public: 
    Animal(string _name): 
    name(_name) 
    { 

    } 

    virtual void eat() 
    { 
     cout << "An animal " << name << " is eating." << endl; 
    } 
}; 

class Dog : public Animal 
{ 

public: 

    Dog(string _name): 
    Animal(_name) 
    { 

    } 

    void eat() 
    { 
     cout << "A dog " << name << " is eating." << endl; 
    } 
}; 

int main(int argc , char ** argv) 
{ 
    Animal a("A"); 
    a.eat(); 

    Dog b("b"); 
    b.eat(); 

    Animal & c = a; 
    c.eat(); 

    c = b; 
    c.eat(); 

    return 0; 
} 

這是輸出:

An animal A is eating. 

A dog b is eating. 

An animal A is eating. 

An animal b is eating. 
+1

我看不出參考'D'在你的代碼。 – taocp

+0

@taocp,對不起,它是c,不是d。我已經修好了。 –

+0

3個答案在12秒內.... :) – dyp

回答

1

爲了利用虛擬函數提供的動態多態性(在運行時期間區分派生類和基類),需要通過基類指針或引用來訪問派生類對象。

我註釋掉的代碼,其中的混亂可能會發生:

int main(int argc , char ** argv) 
{ 

    Animal a("A"); 
    a.eat(); 

    Dog b("b"); 
    b.eat(); 

    // Make a reference (alias) to Animal object and set it to the object a. 
    // From this point on, whenever you write c, think "a". 
    Animal & c = a; 
    // So, this is a.eat() 
    c.eat(); 

    // This is a = b (Animal = Dog): DANGER! SLICING! Here, the assignment operator 
    // slices the derived object and only assigns the base object "part" (remember, 
    // read "a", where you see "c" in your code): 
    // a.operator=(const A& b) 
    c = b; 
    // a.eat() = a is object of type A, so naturally, here you call A::eat() 
    c.eat(); 

    return 0; 
} 
+0

非常感謝您的澄清。 –

2

因爲你不能重新綁定引用。一旦你初始化了它們,它們的名字總是指你已經初始化它們的對象。

一個對象可以有一個名字,例如, Animal a("A");創建Animal類型的對象並引入引用此對象的名稱a。在另一方面

參考引進名,而不會引入對象(讓我們不考慮臨時變量):

Animal& c = a; // a new name `c` which refers to the same object as `a` 

// another (evil) example: 
Animal& c = *(new Animal("C")); // `new` introduces an object without name 
           // `c` now refers to this object 

關於分配:

Animal & c = a; 
// the name `c` is now equivalent to the name `a` 

c = b; // equivalent to `a = b;` 

這最後的分配由b需要引用的對象,並將Animal類型的子對象複製到c所引用的對象中。由於ac是等效的,這就是a所指的相同對象。因此,a.name設置爲"B"

虛擬函數調用當然c.eat()上的ID-表達式(c),其動態類型是Animal操作 - 類型相同a - 因此,Animal::eat稱爲代替Dog::eat

+0

非常感謝你的詳細解釋。 –

3
Animal & c = a; 
c.eat(); 

c = b; ///^^^ 
c.eat(); 

在C++中,引用不能重新綁定到其他對象,一旦它被初始化。 c仍然是對象a的別名,因此它是Animal,因此您看到了預期的輸出。

+0

非常感謝。 :) –

+0

@takwing歡迎您! – taocp

3

對於對象,引用是別名。在綁定對象的引用(並且必須必須在初始化時發生),您在引用上所做的操作是在被引用的對象上完成的。

特別是,您不能重新綁定已經綁定到對象的引用,並讓它引用不同的對象。因此,下面的任務(因爲這是一個分配,未初始化):

c = b; 

等同於以下內容:

a = b; 

由於c是反對a參考。上述分配結果爲slicing,這不是您想要的:c不會與b綁定的引用,但它仍然是綁定到a的引用,已將其分配給b

+0

在這個特定的背景下,切片術語意味着什麼? 這是否意味着只有派生類Dog的對象的「動物」部分被複制到? –

+0

@takwing:的確如此。另請參閱[本文](http://en.wikipedia.org/wiki/Object_slicing)以獲得更詳細的解釋 –

+0

非常感謝您的鏈接。 –

1

你不能重新綁定的參考,一旦你已經束縛它,所以你必須改用引用指針:

Animal *c = &a; 
c->eat(); 

c = &b; 
c->eat(); 

現在將工作完全因爲你想它。

+0

非常感謝您的回答:) –