Usually, when people talk about polymorphism in programming, they try to define this word in terms of programming languages.
However, in my opinion a better definition of polymorphism is given in biology. Only the word организм in this definition should be replaced by the word объект in terms of programming. :)
Polymorphism in biology (from the ancient Greek πολύμορφος - diverse) - the ability of some organisms to exist in states with different internal structure or in different external forms
Your demonstration program defines a pointer of type Base * , that is, the static type of objects addressed by this pointer is type Base .
Base *pBase;
When an address of an object of a derived class is assigned to such a pointer, the object accessed via this pointer is considered as an object of type Base . In fact, all the information that this object is actually a derived class, and not the base class, is lost. as the static pointer type Base * .
However, due to the presence of a tool such as virtual functions, it allows to obtain a variety of behavior of an object addressed by the pointer, depending on what type the object actually has. That is, through virtual functions, it is possible to distinguish between the actual type of addressable objects and access their unique properties. And this is demonstrated by your program on the example of data sentences.
Base *pBase = &derived;//Объявили указатель на базовый класс, присвоив ему ссылку на объект класса наследника pBase->Virtual_method(); // Вызов виртуального метода pBase->Nonvirtual_method(); // Вызов не виртуального метода
As you can see, the Base * type pointer actually addresses the object of the derived class. But if you call a non-virtual method
pBase->Nonvirtual_method(); // Вызов не виртуального метода
then all information about the derived class is not available, since the compiler calls functions according to the static type of the object addressed by the pointer, that is, according to the Base type. The compiler when searching for the name of a function that should be called is looking at the definition of the base class. He does not know anything about derived classes.
If you call a virtual function
pBase->Virtual_method(); // Вызов виртуального метода
then the compiler also looks for the base class when searching for the name of the function being called. However, there is some focus. The derived class has replaced the definition of a virtual function in the base class with its definition of this function. Technically, this is done as follows. If the class declares a virtual function, then it creates a table of pointers to virtual functions declared in its definition. Derived classes, which override virtual functions, substitute addresses of their function definitions into this base class table. Therefore, dynamically during program execution, the function whose address is in the virtual function address table is called.
As for your question
In which cases it is necessary to use a pointer to the class heir, and in which to the base class?
When you declare a pointer to a class of successor, you lose polymorphism, since you can work only with objects of the class of the heir (I assume that the heir, in turn, is not inherited by other classes). And when you want to achieve behavior consistent with polymorphism, then
1) the base class should contain virtual functions and 2) it should declare a pointer or a link that has a static type of pointer or a link to objects of the base class, and initialize them with objects of derived classes.
And then you get
the ability of some objects (organisms) to exist in states with different internal structures or in different external forms
Note If when you call a virtual function, you specify its qualified name, then the “virtuality” disappears. The class function defined in the same class is called.
Consider an example.
#include <iostream> struct Base { virtual ~Base() { } virtual void virtual_function() const { std::cout << "Base::virtual_function() is called" << std::endl; } }; struct Derived : Base { void virtual_function() const override { std::cout << "Derived::virtual_function() is called" << std::endl; } }; int main() { Derived d; Base &rBase = d;; rBase.virtual_function(); rBase.Base::virtual_function(); return 0; }
Her console output will be as follows.
Derived::virtual_function() is called Base::virtual_function() is called
In the first case, polymorphism comes into play. The overridden virtual function of the derived class Derived is called, although the base type of the reference is the class Base .
In the second case, when a qualified name is used, the “virtuality” of the function disappears, and the implementation of the function of the base class Base called.