Learning C++: polymorphism and slicing

C++Polymorphism

C++ Problem Overview


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.

C++ Solutions


Solution 1 - C++

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).

Solution 2 - C++

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

When you instantiate a Dog and assign it by-value to an Animal variable, you slice the object. Which basically means you strip off all the Dog-ness from badDog and make it in to the base class.

In order to use polymorphism with base classes, you must use either pointers or references.

Solution 3 - C++

You initialized badDog using the assignment operator. Thus Dog() was copied as Animal. The output of your program is correct. :)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJnBrymnView Question on Stackoverflow
Solution 1 - C++James McNellisView Answer on Stackoverflow
Solution 2 - C++John DiblingView Answer on Stackoverflow
Solution 3 - C++Android EveView Answer on Stackoverflow