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.
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.
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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With