Member variable polymorphism and argument by reference

I am new to C ++ and wondering about the polymorphism of member variables. 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 that when I run the following, getNumberOfDoors of the parent class is called. I can get around this problem by pointing the pointer variable Part to a pointer, but I prefer to pass the input by reference instead of the pointer (which, as I understand it, is preferable). Could you tell me what I am doing wrong? Thanks!

 ThreeDoorCar myThreeDoorCar; std::cout << myThreeDoorCar.getNumberOfDoors() << std::endl; CarPrinter carPrinter(myThreeDoorCar); carPrinter.printNumberOfDoors(); 
+6
source share
3 answers

By making a copy of an object, you sacrifice its polymorphic abilities. Whatever type of car you go through, the copy will be of type Car (base class), because that is what is declared as.

If you want to continue using polymorphism, use a pointer or link. Here is the version using the link:

 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 to use the constructor, which takes the link as an argument. (These links should not be const , although const makes sense as long as the CarPrinter target CarPrinter just printed.)

One potentially unwanted side effect is that you cannot change what the link refers to after creating the CarPrinter object. If you need to print information for another object, you need to create a new CarPrinter object for it. These objects would then really act as (probably short-lived) wrappers around links.

If you don't like this, you can still continue passing the reference to the constructor, but turn it into a pointer by taking its address in the constructor implementation, and then save it.

+8
source

When you do:

 Car m_car; 

It will not process the m_car instance polymorphically, even if Car has subclasses and virtual functions. He will use the functions of Car . This is called static binding - it determines which function to call at compile time based on the static type ( Car ).

You need a link or a pointer to process it polymorphically using dynamic sending , looking at the correct virtual function through a table of virtual functions of a dynamic instance type (for example, ThreeDoorCar or TwoDoorCar , etc.) at runtime. The polymorphic behavior of a call is achieved using pointers or references in combination with declarations of virtual functions. This is more or less direct result of syntactic use of vs pointers / refs values โ€‹โ€‹(see @ kfmfe04 Comment below).

 Car* pCar; Car& rCar = x_car; 

Virtual elements called with a pointer or reference (for example, pCar->getNumberOfDoors() or rCar.getNumberOfDoors() ) view the vtable view at runtime (dynamic sending). 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 that statically binds the function address ( Car::getNumberOfDoors ) at compile time.

+3
source

The problem is this line of the CarPrinter constructor:

 : car_(car) 

This calls the compiler-created default constructor for the Car class, which finishes creating the Car instance, not ThreeDoorCar .

Unfortunately, you will need to pass a pointer or pass by reference, but save the pointer. For instance:

 class CarPrinter { public: CarPrinter(const Car& car) :car_(&car) {}; ... protected: const Car* car_; }; 
+1
source

Source: https://habr.com/ru/post/947005/


All Articles