I'm new to C++ and have a question about member variable polymorphism. I have the following class definitions -
class Car
{
public:
Car();
virtual int getNumberOfDoors() { return 4; }
};
class ThreeDoorCar : public Car
{
public:
ThreeDoorCar();
int getNumberOfDoors() { return 3; }
};
class CarPrinter
{
public:
CarPrinter(const Car& car);
void printNumberOfDoors();
protected:
Car car_;
};
and implementation
#include "Car.h"
Car::Car()
{}
ThreeDoorCar::ThreeDoorCar()
{}
CarPrinter::CarPrinter(const Car& car)
: car_(car)
{}
void CarPrinter::printNumberOfDoors()
{
std::cout << car_.getNumberOfDoors() << std::endl;
}
The problem is when I run the following, the getNumberOfDoors of the parent class is called. I can get around this issue by making the member variable Car a pointer, but I prefer to pass in the input by reference instead of by pointer (which I understand to be preferred). Could you tell me what I'm doing wrong? Thanks!
ThreeDoorCar myThreeDoorCar;
std::cout << myThreeDoorCar.getNumberOfDoors() << std::endl;
CarPrinter carPrinter(myThreeDoorCar);
carPrinter.printNumberOfDoors();
In object-oriented programming, a member variable (sometimes called a member field) is a variable that is associated with a specific object, and accessible for all its methods (member functions).
You should always initialize native variables, especially if they are class member variables. Class variables, on the other hand, should have a constructor defined that will initialize its state properly, so you do not always have to initialize them.
Polymorphism in C++ means, the same entity (function or object) behaves differently in different scenarios. Consider this example: The “ +” operator in c++ can perform two specific functions at two different scenarios i.e when the “+” operator is used in numbers, it performs addition.
Polymorphism in C++ Typically, polymorphism occurs when there is a hierarchy of classes and they are related by inheritance. C++ polymorphism means that a call to a member function will cause a different function to be executed depending on the type of object that invokes the function.
By making a copy of the object you sacrifice its polymorphic abilities. Whatever type of car you pass, the copy will be of type Car
(the base class), because that is what it is declared as.
If you want to keep using polymorphism either use a pointer or a reference. Here is the version using a reference:
class CarPrinter
{
public:
CarPrinter(const Car& car);
void printNumberOfDoors();
protected:
const Car &car_; // <<= Using a reference here
};
As you can see, this way you can continue using a constructor that takes a reference as argument. (These references don't have to be const
, although const
makes sense as long as the purpose of the CarPrinter
is just printing.)
One potentially undesirable side-effect of this is that you can't change what the reference refers to after constructing the CarPrinter
object. If you need to print the information for a different object, you'll have to create a new CarPrinter
object for that. These objects would then really just act as (probably short-lived) wrappers around references.
If you don't like this, you can still continue passing a reference to the constructor, but turn it into a pointer by taking its address in the constructor implementation and then storing that.
When you do:
Car m_car;
It will not treat the m_car
instance polymorphically, even if Car
has subclasses and virtual functions. It will just use Car
functions. This is called static binding - it determines which function to call at compile time based on the static type (Car
) .
You need a reference or pointer for it to be handled polymorphically via dynamic dispatch by looking up the correct virtual function via the virtual function table of the instance's dynamic type (e.g. ThreeDoorCar
or TwoDoorCar
etc) at runtime. Polymorphic call behaviour is achieved through pointers or references in combination with virtual function declarations. This is more or less a direct result of syntactically using values vs pointers/refs (See @kfmfe04's comment below).
Car* pCar;
Car& rCar = x_car;
Virtual members called via a pointer or reference (e.g. pCar->getNumberOfDoors()
or rCar.getNumberOfDoors()
) does a vtable lookup at run time (dynamic dispatch). Because only at runtime does it know the dynamic type of the instance.
But m_car.getNumberOfDoors()
is a virtual member that is called directly, and the compiler knows at compile time the direct (static) type and function address, statically binding the function address (Car::getNumberOfDoors
) at compile time.
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