Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Learning C++: polymorphism and slicing

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.

like image 780
JnBrymn Avatar asked Dec 09 '10 22:12

JnBrymn


2 Answers

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

like image 118
James McNellis Avatar answered Oct 09 '22 10:10

James McNellis


 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.

like image 26
John Dibling Avatar answered Oct 09 '22 09:10

John Dibling