Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial programming >> Язык C

Виртуальные функции в C++: концепции, примеры и ключевое слово override

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

Это руководство объясняет виртуальные функции в C++, их назначение и практическое использование на примерах кода.

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

Когда базовый класс содержит невиртуальную функцию, вызов её через указатель на базовый класс всегда выполнит базовую версию, независимо от реального типа объекта. Рассмотрим:

class Base {
public:
    void print() { // базовая реализация }
};
class Derived : public Base {
public:
    void print() { // производная реализация }
};

В программе ниже Base* указывает на экземпляр Derived, но print() разрешается в базовую версию:

int main() {
    Derived derived1;
    Base* base1 = &derived1;
    base1->print(); // вызывает Base::print()
    return 0;
}

Для получения полиморфного поведения объявите функцию как virtual в базовом классе:

class Base {
public:
    virtual void print() { // базовая реализация }
};

Виртуальные функции — основа полиморфизма в C++. Для глубокого погружения см. наше руководство C++ Полиморфизм.


Пример 1: Базовое использование виртуальной функции

#include <iostream>
using namespace std;

class Base {
public:
    virtual void print() {
        cout << "Base Function" << endl;
    }
};

class Derived : public Base {
public:
    void print() override {
        cout << "Derived Function" << endl;
    }
};

int main() {
    Derived derived1;
    Base* base1 = &derived1;
    base1->print(); // печатает Derived Function
    return 0;
}

Вывод

Derived Function

Так как Base::print виртуальна, вызов диспетчеризуется в Derived::print даже через указатель на базовый класс.

Виртуальные функции в C++: концепции, примеры и ключевое слово override

Идентификатор override в C++

Начиная с C++11, ключевое слово override помогает предотвратить случайные ошибки переопределения, заставляя компилятор проверить, что функция действительно переопределяет виртуальную функцию базового класса.

Пример:

class Base {
public:
    virtual void print() { /* базовая реализация */ }
};

class Derived : public Base {
public:
    void print() override { /* производная реализация */ }
};

Если определение вынесено за пределы класса, используйте:

class Derived : public Base {
public:
    void print() override; // объявление
};

void Derived::print() { // реализация
    // ...
}

Безопасное использование override

Без override опечатка или несовпадение сигнатуры молча создаёт новую функцию вместо переопределения, что приводит к тонким багам. Компилятор выдаст ошибку, делая проблему сразу видимой. Типичные подводные камни:


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

Рассмотрим базовый класс Animal с производными Dog и Cat. Каждый класс имеет член type, инициализируемый конструктором.

class Animal {
private:
    string type;
public:
    Animal() : type("Animal") {}
    virtual string getType() const { return type; }
};

class Dog : public Animal {
private:
    string type;
public:
    Dog() : type("Dog") {}
    string getType() const override { return type; }
};

class Cat : public Animal {
private:
    string type;
public:
    Cat() : type("Cat") {}
    string getType() const override { return type; }
};

С виртуальной getType() одна вспомогательная функция может печатать любое животное:

void print(Animal* ani) {
    cout << "Animal: " << ani->getType() << endl;
}

Использование в main():

int main() {
    Animal* animal1 = new Animal();
    Animal* dog1 = new Dog();
    Animal* cat1 = new Cat();
    
    print(animal1);
    print(dog1);
    print(cat1);
    
    delete animal1;
    delete dog1;
    delete cat1;
    return 0;
}

Вывод

Animal: Animal
Animal: Dog
Animal: Cat

Делая getType() виртуальной, мы избегаем дублирования логики print() в каждом производном классе и гарантируем доступ к правильному type.

Чисто виртуальные функции и абстрактные классы

Если базовый класс не должен предоставлять реализацию по умолчанию, используйте чисто виртуальную функцию:

class Shape {
public:
    virtual double area() const = 0; // чисто виртуальная
    virtual ~Shape() = default;      // виртуальный деструктор обязателен
};

Класс с хотя бы одной чисто виртуальной функцией становится абстрактным — его нельзя инстанцировать напрямую. Производные классы обязаны реализовать все чисто виртуальные функции.

Таблица виртуальных функций (vtable)

Компилятор реализует виртуальные функции через vtable (таблицу виртуальных функций) — массив указателей на функции. У каждого класса с виртуальными функциями есть своя vtable. У каждого объекта такого класса есть скрытый указатель (vptr) на vtable своего класса. При виртуальном вызове происходит косвенный переход через vptr → vtable → функция. Это добавляет небольшие накладные расходы, но обеспечивает полиморфизм.

Язык C

  1. Как передавать массивы в функции в C++: практическое руководство
  2. Классы и объекты в C++: практическое руководство по объектно-ориентированному программированию
  3. Дружественные функции и классы в C++: мастерство контроля доступа
  4. Шаблоны классов в C++: практическое руководство
  5. Функции в C++: подробное объяснение с практическими примерами кода
  6. Мастерство функций в Verilog для эффективного RTL дизайна
  7. Функции в C: объявление, определение, вызов, аргументы (по значению и по ссылке), рекурсия
  8. Классы хранения в C++: область видимости, время жизни и использование
  9. Перегрузка в C++: функции и операторы — подробное руководство
  10. Полиморфизм в C++: практическое руководство