1

In the following code, why does the last call of eat() on the reference c return "An animal b is eating." ? From my understanding, c is a reference to instance b of the derived class Dog and eat() is a virtual function. So it should have returned "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;
}

This is the output:

An animal A is eating.

A dog b is eating. 

An animal A is eating. 

An animal b is eating.
3
  • 1
    I don't see reference d in your code. Commented May 19, 2013 at 14:07
  • @taocp, sorry it is c, not d. I have already fixed it. Commented May 19, 2013 at 14:09
  • 3 answers within 12 seconds.... :) Commented May 19, 2013 at 14:13

5 Answers 5

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

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

In C++, reference cannot be rebind to other object once it is being initialized. c is still an alias of object a, which is an Animal, therefore, you saw the output which is expected.

Sign up to request clarification or add additional context in comments.

Comments

3

A reference is an alias for an object. After you bind a reference to an object (and that must happen at initialization time), what you do on the reference is done on the object being referenced.

In particular, you cannot re-bind a reference that has been already bound to an object and let it reference a different object. Thus, the following assignment (because that's an assignment, not an initialization):

c = b;

Is equivalent to the following:

a = b;

Since c is a reference to object a. The above assignment results in slicing, which is not what you wanted: c won't be a reference bound to b, but it will still be a reference bound to a, to which b has been assigned.

2 Comments

What does the term slicing mean in this particular context? Does it mean that only the "Animal" part of the object of the derived class Dog is copied to a?
@takwing: Yes, indeed. Also see this article for a more detailed explanation
2

Because you can't rebind references. Once you initialized them, their name always refers to the object you have initialized them with.

An object can have a name, e.g. Animal a("A"); creates an object of type Animal and introduces a name a which refers to this object.

References on the other hand introduce names without introducing objects (let's not consider temporaries):

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

Concerning the assignment:

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

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

This last assignment takes the object referred to by b, and copies its sub-object of type Animal to the object which c refers to. As a and c are equivalent, that's the same object a refers to. Therefore, a.name is set to "B".

The virtual function call c.eat() of course operates on an id-expression (c) whose dynamic type is Animal - the same type as a - therefore, Animal::eat is called instead of Dog::eat.

Comments

1

In order to make use of the dynamic polymorphism provided by virtual functions (distinguishing between derived and base classes during runtime), you need to access the derived class object via the base class pointer or reference.

I've commented out your code where confusion might have taken place:

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;
}

Comments

1

You cannot re-bind a reference once you have bound it, so you have to use pointers instead of references:

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

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

Now it will work exactly as you have wished it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.