4

I'm studying C++ form Thinking in C++ V1. I came across an example that demonstrates inheritance. Here it goes

#include <iostream>

class Instrument{
public:
    virtual void play(){
        std::cout<<"instrument::play()";
    }
};

class Wind: public Instrument{
public:
    void play(){
        std::cout<<"Wind::play()";
    }
};

void tune(Instrument& i){
    i.play();
}
int _tmain(int argc, _TCHAR* argv[])
{
    Wind flute;
    tune(flute);
    return 0;
}

This outputs Wind::play() on the console.

But if I change the method 'tune' to

void tune(Instrument i){
    i.play();
}

The output will instrument::play()

Since the '&' is added so that the reference of flute is passed instead of a copy, why does the program output instrument::play() instead of Wind::play() ?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
Ravi Shenoy
  • 467
  • 1
  • 4
  • 14

5 Answers5

17

Because the copy that gets passed has type Instrument, not type Wind, because Instrument is the type that the function takes. This is known as "slicing".

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • This is really the most correct answer- an `Instrument` can only ever be an `Instrument`. – Puppy Oct 09 '13 at 18:06
7

The polymorphic behavior of an object expression in C++ is determined by its dynamic type.

  • When you attach a reference of type Instrument &i to an object of type Wind, the dynamic type of the object referred by i is preserved. I.e. even though i is declared with type Instrument, it actually refers to an actual object of type Wind. And that is why i continues to behave as Wind. The same would be true if you refer to some object with a pointer.

    Speaking in formal language, it means that static type of expression i is Instrument, but dynamic type if the same expression is Wind. For this reason, i behaves polymorphically as Wind.

  • Meanwhile, when you assign (or initialize) an object Instrument i with a value of type Wind, you create an independent standalone object i of type Instrument. The type of this object is Instrument. So that standalone copy i will behave as Instrument in all polymorphic contexts.

    Again, speaking in formal language, it means that in this case both static type and dynamic type of expression i is Instrument. For this reason, i behaves polymorphically as Instrument.

In C++ language the only way to "preserve" a dynamic type of the object is to refer to that object with pointers or references. "Refer" is the key word here: you do not create new objects, but rather refer to already existing objects. And those existing objects continue to behave in accordance with their native type. An object of type Wind continues to behave as an object of type Wind. (Note that this applies to polymorphic behavior only, e.g. to how virtual function calls are resolved.)

But whenever you create an independent copy of an object (instead of referring to the original), that copy acquires a life of its own, with its own type and related properties. A standalone copy of type Instrument has no relation to the original Wind object. It has no memory of the fact that it was born as a copy of a Wind object. It behaves as an Instrument.

Community
  • 1
  • 1
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
3

This is the problem called "slicing". When you copy Wind into an Instrument it becomes an Instrument.

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
0

In

void tune(Instrument i){
    i.play();
}

You create a new instance if the Instrument object, and the Instrument object will print instrument::play

When you used the reference operator & you referred to an existing object that was of the Wind type.

Grzegorz
  • 3,207
  • 3
  • 20
  • 43
-2

The reason that the Instrument::play function is called, is because when you pass a copy, it's actually the Instrument part of the object you pass, since you slice the object.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621