Виртуальные функции - IsuiGit/borodaedu GitHub Wiki

Пример нарушения вызовов метода класса-наследника

#include <iostream>

class Person {
public:
    Person(std::string name) : name(name) {};
    void print() { std::cout << "Name: " << name << std::endl; }
private:
    std::string name;
};

class Employee : public Person {
public:
    Employee(std::string name, std::string company) : Person(name), company(company) {};
    void print() { Person::print(); std::cout << company << std::endl; }
private:
    std::string company;
};

int main()
{
    // Создание экземпляров класса и вызов внутренних методов...OK!
    Person tom("tom");
    tom.print();
    Employee bob("bob", "Microsoft");
    bob.print();
    std::cout << "Objects tests passed\n";
    // Создание указателей на тип Person и вызов print()...failed!
    Person kate("kate");
    Person* person{ &kate };
    std::cout << "Kate (Person) calls print()\n";
    person->print();
    Employee andrew("andrew", "Amazon");
    person = &andrew;
    std::cout << "Andrew (Employee) calls print()\n";
    person->print();
}

Виртуальные функции

Виртуальная (virtual) функция - тип функций классов, предназначенные для динамического связывания реализаций методов между собой, для построения правильного стека вызовов наследных классов.

#include <iostream>

class Person {
public:
    Person(std::string name) : name(name) {};
    virtual void print() { std::cout << "Name: " << name << std::endl; } // <- теперь эта функция виртуальная и динамически связана с классами-наследниками
private:
    std::string name;
};
...

Ключевое слово override

Override - ключевая лексема в явном виде указывающая компилятору на перезапись виртуальной функции на этапе компиляции. Необходимо для отмены эффекта затенения (затирания), предусмотренного в качестве механизма защиты при компиляции.

#include <iostream>

class Person {
public:
    Person(std::string name) : name(name) {};
    virtual void print() { std::cout << "Name: " << name << std::endl; } // <- теперь эта функция виртуальная и динамически связана с классами-наследниками
private:
    std::string name;
};

class Employee : public Person {
public:
    Employee(std::string name, std::string company) : Person(name), company(company) {};
    void print() override { Person::print(); std::cout << company << std::endl; } // <- а эта функция защищена от затирания с помощью override
private:
    std::string company;
};
...

Принцип выполнения виртуальных функций

  1. На этапе компиляции создается специальная таблица vtable.
  2. В объектах, использующих virtual функции, создается указатель на адрес описания функции в классе.
  3. Виртуальная функция ссылается на таблицу vtable, и находит конкретную реализацию для конкретного класса, в текущем контексте.

Схема выполнения виртуальных функций

image

Запрет переопределения

final - спецификатор, определяющий запрет на изменение виртуальных функций в производных классах, с тем же именем и сигнатурой.

class Person{
public:
    virtual void print() final{
        ...
    }
}
class Employee{
public:
    void print() override { // <- компилятор выдаст ошибку переопределения функции защищенной от перезаписи
        ...
    }
}
⚠️ **GitHub.com Fallback** ⚠️