Could you comment on this point:

Using virtual functions to ensure polymorphism, you must use a pointer specifically for the base class.

In which cases it is necessary to use a pointer to the class heir, and in what the base class?

Just recently stumbled upon such an example, is it correctly implemented?

#include <iostream> using namespace std; class Base { //Базовый класс public: int base_data; Base(int base_data);//Конструктор класса virtual void Virtual_method(); // Виртуальный метод void Nonvirtual_method(); // Не виртуальный метод virtual~Base(); //Виртуальный деструктор }; Base::Base(int base_data){ this->base_data = base_data; cout << "Конструктор базового" << endl; } void Base::Virtual_method() { cout << "Виртуальный метод базового класса\n"; } void Base::Nonvirtual_method() { cout << "Не виртуальный метод базового класса\n"; } Base::~Base(){ cout << "Деструктор базового класса\n"; } class Derived : public Base {//Класс наследник public: double derived_data; Derived(double derived_data, int base_data); void Virtual_method(); // Переопределённый виртуальный метод базового класса void Nonvirtual_method(); //Не виртуальна функция ~Derived(); }; Derived::Derived(double derived_data, int base_data):Base(base_data){ this->derived_data = derived_data; cout << "Конструктор наследника" << endl; } void Derived::Virtual_method() { cout << "Переопределённый виртуальный метод класса наследника\n"; } void Derived::Nonvirtual_method() { cout << "Не виртуальный метод класса наследника\n"; } Derived::~Derived(){ cout << "Деструтор класса наследника\n"; } int main() { setlocale(LC_ALL, "RUS"); Derived derived(2.0, 1);//Объявили объект класса наследника Derived *pDerived = &derived;//Объявили указатель на класс наследник, присвоив ему ссылку на объект класса наследника Base *pBase = &derived;//Объявили указатель на базовый класс, присвоив ему ссылку на объект класса наследника pBase->Virtual_method(); // Вызов виртуального метода pBase->Nonvirtual_method(); // Вызов не виртуального метода pDerived->Virtual_method(); // Вызов виртуального метода pDerived->Nonvirtual_method(); // Вызов не виртуального метода cout << "base_data = " << derived.base_data << "\nderived_data = " << derived.derived_data << "\n"; return 0; } 

    2 answers 2

    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.

    • one
      @Brainsick It is correct in all cases where the pointer points to a Derived object or an object derived from it. Another thing is that there will be no polymorphism if this pointer points to an object of type Derived. The highlight of your program is that having a pointer to Base we can call the implementation of a virtual function of any derived class, if we assign this pointer the address of the object of the derived class. That is, it looks like Base objects behave differently depending on which derived class the object is addressed by a pointer or a link. - Vlad from Moscow
    • one
      @Brainsick Yes, there will be no polymorphism, since the same implementation of the function defined in the Derived class will be called. The call will look exactly the same way as if this function were not virtual. - Vlad from Moscow
    • one
      @Brainsick The idea of ​​polymorphism is that by calling a function declared in the base class, we call functions in derived classes that override the function declared in the base class if we have a pointer or a reference to an object of the derived class, although the type of reference or pointer has static type related to the base type. - Vlad from Moscow
    • one
      @Brainsick See also the addition to my answer. - Vlad from Moscow
    • one
      @Brainsick Qualified name is Base :: virtual_function. This shows how the name deals with the object of the derived class, to call the virtual function of the base class, rather than overriding it in the derived class. - Vlad from Moscow

    The mechanism of virtual functions allows for the so-called "late binding". Those. what specific function will be called becomes known only at the moment of program execution. In code, it looks like a virtual function call through a pointer or a reference to the base class.

    So the base class should be used when it is not known in advance what function call (i.e. what specific heir) is needed.

    If you know in advance which code should be called, then the mechanism of virtual functions is not required. Those. call the non-virtual function on the type on which we want it to be called.