Search code examples
c++classinheritancemethodsvirtual

Why does the subclass call its parent's method instead of it's own?


I am learning c++ on my own and have encountered a behavior I did not expect. I am unsure but do think this is not what Java would do in a similar situation. Why are the Dog and the Cat relying on te parent class implementation and making a generic sound?

#include <iostream>

class Animal{
    public:
        virtual void speak(){std::cout << "???" << std::endl; /* not implemented */}
};

class Dog : public Animal{
    public:
        virtual void speak(){
            std::cout << "Woof" << std::endl;
        }
};

class Cat : public Animal{
    public:
        virtual void speak(){
            std::cout << "Meow" << std::endl;
        }
};

class Desk{
    /* Not an animal */
};

template<class T>
void please_talk(){
    T anim;
    Animal ani = anim;
    anim.speak();
    ani.speak();
}

int main()
{
    please_talk<Dog>();
    please_talk<Cat>();
    /* Does not compile please_talk<Desk>(); */
    return 0;
}

Result:

Woof
Generic animal sound
Meow
Generic animal sound

Solution

  • Animal ani = anim;
    

    That creates a new object of type Animal, regardless of the type of anim. So ani.speak() will call Animal::speak, since that is the override for type Animal.

    If you were to create a reference (or pointer)

    Animal & ani = anim;
    

    then the dynamic type would be preserved, and T::speak would be called. This is similar to what happens in Java when you copy an object reference; but you should not try to understand the C++ object model in terms of Java's since they are very different.

    You can prevent accidental creation of base-class objects (sometimes known as "slicing", since it slices off the derived-class parts of an object) by making the base class abstract. That is, declare the function pure virtual, rather than non-pure with a default implementation:

    virtual void speak() = 0; // Really not implemented
    

    Abstract classes can't be instantiated as a complete object, so this will cause a compile-time error if you try to.