Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't I do polymorphism with normal variables?

I'm a Java programmer and recently started studying C++. I'm confused by something.

I understand that in C++, to achieve polymorphic behavior you have to use either pointers or references. For example, consider a class Shape with an implemented method getArea(). It has several subclasses, each overriding getArea() differently. Than consider the following function:

void printArea(Shape* shape){
    cout << shape->getArea();
}

The function calls the correct getArea() implementation, based on the concrete Shape the pointer points to.

This works the same:

void printArea(Shape& shape){
    cout << shape.getArea();
}

However, the following method does not work polymorphicaly:

void printArea(Shape shape){
    cout << shape.getArea();
}

Doesn't matter what concrete kind of Shape is passed in the function, the same getArea() implementation is called: the default one in Shape.

I want to understand the technical reasoning behind this. Why does polymorphism work with pointers and references, but not with normal variables? (And I suppose this is true not only for function parameters, but for anything).

Please explain the technical reasons for this behavior, to help me understand.

like image 397
Aviv Cohn Avatar asked Oct 03 '14 23:10

Aviv Cohn


3 Answers

The answer is copy semantics.

When you pass an object by value in C++, e.g. printArea(Shape shape) a copy is made of the object you pass. And if you pass a derived class to this function, all that's copied is the base class Shape. If you think about it, there's no way the compiler could do anything else.

Shape shapeCopy = circle;

shapeCopy was declared as a Shape, not a Circle, so all the compiler can do is construct a copy of the Shape part of the object.

like image 77
Jonathan Potter Avatar answered Oct 09 '22 12:10

Jonathan Potter


When void printArea(Shape shape) is called, your object is copied into a brand new Shape on the stack. The subclass parts are not copied. This is known as object slicing. If the base-class object is not legit (e.g. it has pure virtual functions in it), you can't even declare or call this function. That's "pass by value" semantics; a copy of the passed-in object is supplied.

When void printArea(Shape& shape) is called, a reference to your Circle or Rectangle object is passed. (Specifically, a reference to the Shape part of that object. You can't access the Circle- or Square-specific members without casting. But virtual functions work correctly, of course.) That's "pass by reference" semantics; a reference to the original object is passed in.

like image 30
StilesCrisis Avatar answered Oct 09 '22 10:10

StilesCrisis


You're talking about run-time polymorphism.

This is the idea that an object can be of a class derived from the *statically known class, so that calls to virtual member functions in the statically known class, end up in derived class implementations.

With a pointer or reference the most derived class can be different from (more specific than) the statically known class. But with a direct variable the statically known class is the most derived class. So there's no room for run-time polymorphism, except in calls that end up causing calls from base class member functions (in a base class member function the statically known type is that base class, different from the most derived class).


*) Statically known class means known to the compiler without any analysis, the declared class.
like image 39
Cheers and hth. - Alf Avatar answered Oct 09 '22 11:10

Cheers and hth. - Alf