1

在下面的代码中,为什么最后一次调用引用c上的eat()会返回“ An animal b is eating. ”?据我了解,c是对派生类Dog的实例b的引用,而eat()是一个虚函数。所以它应该返回“A dog b is eating”。

#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.
4

5 回答 5

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

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

在 C++ 中,引用一旦被初始化就不能重新绑定到其他对象。c仍然是 object 的别名a,它是 an Animal,因此,您看到了预期的输出。

于 2013-05-19T14:12:29.103 回答
3

引用是对象的别名。将引用绑定到对象后(这必须在初始化时发生),您对引用所做的操作将在被引用的对象上完成

特别是,您不能重新绑定已经绑定到对象的引用并让它引用不同的对象。因此,下面的赋值(因为这是一个赋值,而不是初始化):

c = b;

等价于以下内容:

a = b;

因为c是对 object 的引用a。上面的赋值导致slicing,这不是你想要的:c不会是绑定到 的引用b,但它仍然是绑定到 的引用ab已分配给它。

于 2013-05-19T14:12:33.430 回答
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。和是等价的,指的是同一个a对象。因此,设置为。caa.name"B"

虚函数调用c.eat()当然是在c动态类型为Animal- 与 - 相同的类型的 id-expression () 上运行的a,因此,Animal::eat它被调用而不是Dog::eat.

于 2013-05-19T14:12:21.297 回答
1

绑定后不能重新绑定引用,因此必须使用指针而不是引用:

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

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

现在它将完全按照您的意愿工作。

于 2013-05-19T14:15:47.910 回答
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;
}
于 2013-05-19T14:17:20.320 回答