Learning C++: polymorphism and slicing

JnBrymn picture JnBrymn · Dec 9, 2010 · Viewed 7.8k times · Source

Consider the following example:

#include <iostream>
using namespace std;

class Animal
{
public:
    virtual void makeSound() {cout << "rawr" << endl;}
};

class Dog : public Animal
{
public:
    virtual void makeSound() {cout << "bark" << endl;}
};

int main()
{
    Animal animal;
    animal.makeSound();

    Dog dog;
    dog.makeSound();

    Animal badDog = Dog();
    badDog.makeSound();

    Animal* goodDog = new Dog();
    goodDog->makeSound();
}

The output is:

rawr
bark
rawr
bark

But I thought that surely the output should be "rawr bark bark bark". What's with the badDog?


Update: You may be interested in another question of mine.

Answer

James McNellis picture James McNellis · Dec 9, 2010

This is a problem called "slicing."

Dog() creates a Dog object. If you were to call Dog().makeSound(), it would print "bark" as you expect it to.

The problem is that you are initializing the badDog, which is an object of type Animal, with this Dog. Since the Animal can only contain an Animal and not anything derived from Animal, it takes the Animal part of the Dog and initializes itself with that.

The type of badDog is always Animal; it can never be anything else.

The only way you can get polymorphic behavior in C++ is using pointers (as you have demonstrated with your goodDog example) or using references.

A reference (e.g., Animal&) can refer to an object of any type derived from Animal and a pointer (e.g., Animal*) can point to an object of any type derived from Animal. A plain Animal, however, is always an Animal, nothing else.

Some languages like Java and C# have reference semantics, where variables are (in most cases) just references to objects, so given an Animal rex;, rex is really just a reference to some Animal, and rex = new Dog() makes rex refer to a new Dog object.

C++ doesn't work that way: variables don't refer to objects in C++, variables are objects. If you say rex = Dog() in C++, it copies a new Dog object into rex, and since rex is actually of type Animal, it gets sliced and just the Animal parts get copied. These are called value semantics, which are the default in C++. If you want reference semantics in C++, you need to explicitly use references or pointers (neither of these are the same as references in C# or Java, but they are more similar).