2013-05-19 29 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

非常感谢您的回答:) –