Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Виртуальные функции и полиморфизм в C++
Виртуальные функции — один из столпов объектно-ориентированного программирования в C++, обеспечивающий полиморфизм во время выполнения.
Как это работает
class Animal {
public:
virtual void sound() { std::cout << "Generic sound"; }
virtual ~Animal() {} // Всегда виртуальный деструктор!
};
class Dog : public Animal {
public:
void sound() override { std::cout << "Woof!"; }
};
class Cat : public Animal {
public:
void sound() override { std::cout << "Meow!"; }
};
void make_sound(Animal* animal) {
animal->sound(); // Вызовет нужную функцию в runtime
}
Таблица виртуальных функций (vtable)
Компилятор создаёт для каждого класса с виртуальными функциями таблицу виртуальных функций:
- Каждый объект хранит скрытый указатель на vtable
- vtable содержит указатели на реальные функции
- При вызове виртуальной функции: получаем указатель из vtable и вызываем
class Base {
virtual void foo();
virtual void bar();
};
// Выглядит примерно так:
// Base::vtable[0] = &Base::foo
// Base::vtable[1] = &Base::bar
class Derived : public Base {
void foo() override;
};
// Derived::vtable[0] = &Derived::foo
// Derived::vtable[1] = &Base::bar
Типы виртуальных функций
pure virtual (абстрактные функции):
class Shape {
public:
virtual double area() = 0; // Нет реализации
virtual ~Shape() = default;
};
// Shape s; // ОШИБКА - нельзя создать экземпляр абстрактного класса
class Circle : public Shape {
public:
double area() override { return 3.14 * r * r; }
private:
double r;
};
override и final (C++11+):
class Base {
public:
virtual void func1();
virtual void func2();
};
class Derived : public Base {
public:
void func1() override; // Правильно переопределяем
void func2() override final; // Запрещаем переопределение в потомках
};
class BadDerived : public Derived {
public:
void func2() override; // ОШИБКА КОМПИЛЯТОРА!
};
Производительность
Стоимость виртуальных функций:
- Дополнительный указатель в каждом объекте (8 байт на 64-bit системе)
- Косвенный вызов вместо прямого (несколько дополнительных тактов CPU)
- Может помешать inlining
// Быстро - компилятор inline-ит
class Base {
public:
void non_virtual() { std::cout << "fast"; }
};
// Медленнее - виртуальный вызов требует поиска в vtable
class Base {
public:
virtual void func() { std::cout << "slow"; }
};
Когда это критично:
- Игры, обработка изображений, высокочастотный трейдинг
- Используй devirtualization, специализацию шаблонов, CRTP
Множественное наследование и виртуальные функции
class A {
public:
virtual void func();
};
class B {
public:
virtual void func();
};
class C : public A, public B {
public:
void func() override; // Переопределяет обе!
};
A* a = new C();
B* b = new C();
a->func(); // Вызовет C::func
b->func(); // Вызовет C::func
Важно: указатели a и b будут указывать на разные смещения в объекте!
Лучшие практики
Всегда делай деструктор виртуальным:
// НЕПРАВИЛЬНО
class Base {
virtual void func();
~Base() {} // НЕ виртуальный!
};
class Derived : public Base {
~Derived() { cleanup(); }
};
Base* ptr = new Derived();
delete ptr; // Вызовет Base::~Base(), утечка!
// ПРАВИЛЬНО
class Base {
virtual void func();
virtual ~Base() = default;
};
Используй override:
class Base {
virtual void func();
};
class Derived : public Base {
void func() override; // Компилятор проверит сигнатуру
};
Переходи на CRTP, если нужна производительность:
template<typename Derived>
class Base {
public:
void func() {
static_cast<Derived*>(this)->func_impl();
}
};
class Derived : public Base<Derived> {
public:
void func_impl() { std::cout << "fast"; }
};
Когда использовать
- ✓ Когда нужен полиморфизм и неизвестны типы в compile-time
- ✓ Фреймворки, плагины, расширяемые системы
- ✗ Когда тип известен в compile-time (используй шаблоны)
- ✗ Когда нужна максимальная производительность в critical path