Search code examples
c++inheritancevirtual

Calling a virtual function on a reference


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.

Solution

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