azphare
azphare

Reputation: 525

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?

Upvotes: 15

Views: 7112

Answers (6)

HelpingHand
HelpingHand

Reputation: 1468

A good way to avoid this kind of problems is always to use override or final if you believe that the subclass method you are coding will replace the implementation of a base class method (or provide a first one, if the base class method is pure virtual...). This best-practice rule is also part of industrial coding standards like Autosar C++, where you find well-established rules like

Rule A10-3-1 (required, implementation, automated): Virtual function declaration shall contain exactly one of the three specifiers: (1) virtual, (2) override, (3) final.

If you happen to make a typo (or mismatching const specifier, or just another typing mistake) in the overriding method declared override then the compiler will immediately reject your program and make you think where the typo is.

The opposite case (you want to hide an existing virtual method without overriding it is much less common. Additionally, such a design is considered very bad practice because everybody will confuse the two methods with same/similar names.

Upvotes: 0

Cuong Do
Cuong Do

Reputation: 1

virtual in subclass-->no need. You just add const to function in subclass to make it same signature

Upvotes: 0

joe
joe

Reputation: 349

It took me a while to understand Peter Alexander's hiding answer, but another way to understand it is as follows:

Say that you mispelled the method name in the Cow class, but spelled it correctly in Animal class:

Animal::printMessage()
Cow::mispelledPrintMessage()

then when you have an

Animal *animal;

you can ONLY call

animal->printMessage();

but you CANNOT call

animal->mispelledPrintMessage();

because mispelledPrintMessage() doesn't exist in the Animal class. Its a brand new method in the Cow class, so it cannot be polymorphically called thru a base pointer.

So having the Animal method have const in the signature, but not in Cow method is kinda analogous to a slightly mispelled method name in the derived class.

PS: Another 4th solution (1 making both methods const, 2 making both methods non-const, or 3 using new 2011 override keyword), is to use a cast, to force the Animal pointer into a Cow pointer:

((Cow*)animal)->printMessage();

But this is a very ugly HACK, and I would not recommend it.

PS: I always try to write my toString() methods with const in the signature in the Base class and all derived classes for this very reason. Plus, having const in the toString() signature allows you call toString() either with a const or non-const object. Had you instead left out the const, and tried to pass call toString() with a const object, GCC compiler would complain with the discards qualifiers error message:

const Bad' as `this' argument of `std::string Bad::toString()' discards qualifiers

for this code:

#include <iostream>
#include <string>

class Bad
{
public:
        std::string toString()
        {
                return "";
        }
};

int main()
{
        const Bad       bad;
        std::cout << bad.toString() << "\n";
        return 0;
}

So, in conclusion, since Cow doesn't change any data members, the best solution probably is to add const to printMessage() in derived Cow class, so that both BASE Animal and DERIVED Cow classes have const in their signatures.

-dennis bednar -ahd 310

Upvotes: 1

Ed Heal
Ed Heal

Reputation: 59997

Just tried it. Add the const to the Cow class and it will work.

i.e. virtual void printMessage () const for both classes.

Upvotes: 1

Peter Alexander
Peter Alexander

Reputation: 54270

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.

Upvotes: 19

Mark Ransom
Mark Ransom

Reputation: 308158

You have two different versions of printMessage, one which is const and one which isn't. The two are unrelated, even though they have the same name. The new function in Cow hides the one in Animal, so when you call it directly the compiler only considers the Cow version.

Upvotes: 7

Related Questions