← Назад к вопросам

Что сейчас для себя ищешь?

1.0 Junior🔥 201 комментариев
#Soft skills и мотивация

Комментарии (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