c++inheritancevirtualconstants

virtual function that is const in the base class and not const in the derived


Can anyone explain the output of the following code?

#include <iostream>
#include <string>
class Animal
{
public:
    Animal(const std::string & name) : _name(name) { }
    ~Animal() { }
    virtual void printMessage() const
    {
        std::cout << "Hello, I'm " << _name << std::endl;
    }

private:
    std::string _name;
    // other operators and stuff
};

class Cow : public Animal
{
public:
    Cow(const std::string & name) : Animal(name) { }
    ~Cow() { }
    virtual void printMessage()
    {
        Animal::printMessage();
        std::cout << "and moo " << std::endl;
    }
};

int main() {
    Cow cow("bill");
    Animal * animal = &cow;
    cow.printMessage();
    animal->printMessage();
}

The output is

Hello, I'm bill
and moo
Hello, I'm bill

I don't understand why. The pointer animal points at an object of type Cow. printMessage is a virtual function. Why isn't the implementation of the Cow class the one that is called?


Solution

  • Cow isn't overriding the virtual function from Animal because it has a different signature. What's actually happening is that Cow is hiding the function in Animal.

    The result of this is that calling printMessage on an Animal will just use the version in Animal, regardless of the one in Cow (it isn't overriding it), but calling it from Cow will use the one in Cow (because it hides the one from Animal).

    To fix this, remove the const in Animal, or add the const in Cow.

    In C++ 2011, you will be able to use the override keyword to avoid subtle traps like this:

    class Cow : public Animal 
    { 
    public: 
        Cow(const std::string & name) : Animal(name) { } 
        ~Cow() { } 
        virtual void printMessage() override
        { 
            Animal::printMessage(); 
            std::cout << "and moo " << std::endl; 
        } 
    };
    

    Notice the added override after printMessage(). This will cause the compiler to emit an error if printMessage doesn't actually override a base class version. In this case, you would get the error.