Для чего нужно ключевое слово override в C++?

Ответ

Ключевое слово override (начиная с C++11) явно указывает, что метод переопределяет виртуальный метод базового класса. Это защита от ошибок и улучшение читаемости кода.

Проблема без override:

class Base {
public:
    virtual void draw() const {
        std::cout << "Base drawn";
    }
    virtual void serialize(int version) {
        std::cout << "Base serializen";
    }
};

class Derived : public Base {
public:
    // ОПЕЧАТКА: draw вместо Draw - НЕ переопределение!
    void Draw() const {  // Без override компилятор пропустит
        std::cout << "Derived drawn";
    }

    // ИЗМЕНЕНИЕ СИГНАТУРЫ: забыли const - НЕ переопределение!
    void draw() {  // Без override компилятор пропустит
        std::cout << "Derived draw (non-const)n";
    }
};

Решение с override:

class DerivedSafe : public Base {
public:
    void Draw() const override {  // ОШИБКА КОМПИЛЯЦИИ: нет метода Draw в Base
        std::cout << "Derived drawn";
    }

    void draw() override {  // ОШИБКА КОМПИЛЯЦИИ: сигнатура не совпадает (нет const)
        std::cout << "Derived drawn";
    }

    // Правильные переопределения:
    void draw() const override {  // OK
        std::cout << "Derived drawn";
    }

    void serialize(int version) override {  // OK
        std::cout << "Derived serialize v" << version << "n";
    }
};

Преимущества использования override:

  1. Обнаружение ошибок на этапе компиляции - опечатки, изменение сигнатур
  2. Улучшение читаемости - явно видно, какие методы переопределяют базовые
  3. Поддержка рефакторинга - если изменить метод в базовом классе, компилятор покажет все места, где нужно обновить переопределения

Связанное ключевое слово final:

class Base {
public:
    virtual void cannotOverride() final {}
};

class Derived : public Base {
public:
    void cannotOverride() override {} // ОШИБКА: метод final в Base
};

Ответ 18+ 🔞

А, слушай, про этот override — это вообще отдельная песня, ёпта. Представь себе ситуацию: ты пишешь класс-наследник, думаешь, что всё красиво переопределил, а на деле — нихуя. Опечатка в названии, забытый const, параметр другой — и всё, метод твой летит мимо базового виртуального, и программа работает как попало. А ты потом сидишь и думаешь: «Какого хуя? Я же всё сделал!» Волнение ебать, а найти причину — терпения ноль ебать.

Вот смотри, без override можно накосячить так, что сам от себя охуеешь. Берёшь базовый класс, там метод draw() const. А ты в наследнике, по рассеянности, пишешь Draw() с большой буквы. Компилятор-то молчит, он думает: «А, ну это просто новый метод у наследника, хуй с горы». И в рантайме вызывается метод базового класса, а не твой. Пиздец, да? Или, например, забыл const дописать — и опять мимо. Получается, ты вроде как переопределяешь, а на деле — просто два независимых метода. Хуй в пальто, а не полиморфизм.

А теперь включаем мозги и ставим override. Это как кричать компилятору: «Э, сабака сука, я тут переопределяю метод, проверь, всё ли правильно!» И если ты накосячил с сигнатурой — сразу ошибка компиляции, прямо в лицо. Не «ой, потом разберёмся», а сразу: «Чувак, тут Draw с большой буквы, а в базе — с маленькой, иди исправляй». Удивление пиздец, но зато полезное.

class DerivedSafe : public Base {
public:
    void Draw() const override {  // Компилятор: «Ошибка, мудак! Такого метода в базе нет!»
        std::cout << "Derived drawn";
    }

    void draw() override {  // Компилятор: «И тут ошибка! Где const, блядь?»
        std::cout << "Derived drawn";
    }

    // А вот так — красота:
    void draw() const override {  // Всё чётко, метод найден, сигнатура совпала
        std::cout << "Derived drawn";
    }
};

Преимущества — овердохуища. Во-первых, ошибки ловятся сразу, а не через три недели отладки. Во-вторых, читаемость — видишь override и сразу понимаешь: «Ага, это переопределение, тут связь с базовым классом». В-третьих, рефакторинг — если в базовом классе переименовал метод, компилятор тебе все места, где override сломался, подсветит. Не надо самому по всему коду ползать.

Ну и есть ещё брат-близнец — final. Это когда ты говоришь: «Всё, приехали, дальше этот метод переопределять нельзя, ядрёна вошь». Если в базовом классе метод объявил final, а наследник лезет со своим override — получит ошибку и пойдёт нахуй. Полезно, когда архитектура не должна меняться.

Короче, override — это не просто синтаксический сахар, это реально хитрая жопа, которая спасает от тонких и долбоёбских ошибок. Используй всегда, когда переопределяешь виртуальные методы. И твой код скажет тебе спасибо, а коллеги не будут желать тебе вилкой в глаз.