Ответ
Виртуальная функция — это функция-член класса, объявленная с ключевым словом virtual. Её вызов разрешается не на этапе компиляции (статическое связывание), а во время выполнения программы (динамическое связывание) на основе фактического типа объекта, на который указывает указатель или ссылка. Это основа для реализации полиморфизма в C++.
Как это работает: Компилятор для каждого класса с виртуальными функциями создает скрытую таблицу виртуальных функций (vtable) — массив указателей на реализации. Каждый объект такого класса содержит скрытый указатель (vptr) на vtable своего класса. При вызове виртуальной функции через указатель/ссылку на базовый класс, код во время выполнения следует по vptr, находит в vtable нужный адрес функции и вызывает её.
Ключевые аспекты:
- Переопределение: В производном классе функция с той же сигнатурой переопределяет базовую. Начиная с C++11, рекомендуется использовать спецификатор
overrideдля явного указания намерения. - Чисто виртуальные функции: Функция, объявленная как
virtual ... = 0;. Класс, содержащий такую функцию, становится абстрактным классом, и его нельзя инстанцировать. Производные классы обязаны предоставить её реализацию. - Виртуальный деструктор: Если класс предназначен для наследования и полиморфного использования через указатель на базовый класс, его деструктор должен быть объявлен виртуальным. Иначе при удалении объекта через указатель на базовый класс вызовется деструктор базового класса, но не деструктор производного, что приведет к утечке ресурсов.
Пример:
#include <iostream>
class Shape {
public:
// Виртуальная функция для полиморфного поведения
virtual void draw() const {
std::cout << "Drawing a generic shape.n";
}
// Виртуальный деструктор - КРИТИЧНО для корректного удаления
virtual ~Shape() = default;
};
class Circle : public Shape {
public:
// Переопределение виртуальной функции
void draw() const override { // `override` гарантирует, что функция виртуальна в базовом классе
std::cout << "Drawing a circle.n";
}
};
class Rectangle : public Shape {
public:
void draw() const override {
std::cout << "Drawing a rectangle.n";
}
};
int main() {
Shape* shapes[] = {new Circle(), new Rectangle()};
for (Shape* s : shapes) {
s->draw(); // Вызовется draw() для фактического типа объекта
// Вывод:
// Drawing a circle.
// Drawing a rectangle.
}
for (Shape* s : shapes) {
delete s; // Благодаря виртуальному деструктору вызывается правильный деструктор
}
return 0;
}