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

#include <iostream>
#include <memory>
 
class Fruit
{
public:
  Fruit() = default;
  virtual int size()
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }
  virtual double weight() const
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }
  virtual int color(const int& c)
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return c;
  }
  virtual double volume(const double& v)
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return v;
  }
};
 
class Orange : public Fruit
{
public:
  Orange() = default;
  virtual int size() const override
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }
  virtual double weight() override
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }
  virtual int color(const int c) const override
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return c;
  }
  virtual double volume(const double&& v) override
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return v;
  }
};
 
int main()
{
  std::unique_ptr<Fruit> f = std::make_unique<Orange>();
  f->size();
  f->weight();
  f->color(32);
  f->volume(1.23);
  return 0;
};

Если мы ожидаем, что вывод будет таким:

virtual int Orange::size()
virtual double Orange::weight() const
virtual int Orange::color(const int&)
virtual double Orange::volume(const double&)

то сильно ошибемся, поскольку это показывает нам лишь то, что мы хотим вместо того, что происходит на самом деле. Проблема заключается в том, что ни один из вышеперечисленных методов не был переопределен.
В действительности вывод будет следующим:

virtual int Fruit::size()
virtual double Fruit::weight() const
virtual int Fruit::color(const int&)
virtual double Fruit::volume(const double&)

При правильном подходе к разработке подобные ошибки обычно отлавливаются при модульном тестировании. Но теперь язык облегчает нам жизнь, выдавая их на этапе компиляции. Давайте добавим ключевое слово override в каждый метод наследуемого класса:

class Orange : public Fruit
{
public:
  Orange() = default;
  virtual int size() const override
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }
  virtual double weight() override
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  }
  virtual int color(const int c) const override
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return c;
  }
  virtual double volume(const double&& v) override
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return v;
  }
};

Теперь при компиляции мы получим следующее:

01_override.cpp:32:15: error: ‘virtual int Orange::size() const’ marked ‘override’, but does not override
   virtual int size() const override
               ^
01_override.cpp:36:18: error: ‘virtual double Orange::weight()’ marked ‘override’, but does not override
   virtual double weight() override
                  ^
01_override.cpp:40:15: error: ‘virtual int Orange::color(int) const’ marked ‘override’, but does not override
   virtual int color(const int c) const override
               ^
01_override.cpp:45:18: error: ‘virtual double Orange::volume(const double&&)’ marked ‘override’, but does not override
   virtual double volume(const double&& v) override
                  ^

Подобные ситуации часто возникают на ранних этапах разработки, когда базовые классы имеют нестабильный интерфейс из-за частых изменений сигнатур их методов.
Рассмотрим еще один случай, когда override поможет сэкономить кучу времени и нервов:

#include <iostream>
#include <memory>
 
class Data
{
public:
  Data() = default;
  ~Data()
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  };
};

class Fruit
{
public:
  Fruit() = default;
  ~Fruit()
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
  };
};

class Orange : public Fruit
{
public:
  Orange() : data(new Data)
  {
  }
  virtual ~Orange()
  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    delete data;
  };
private:
  Data* data;
};

int main()
{
  std::unique_ptr<Fruit> p = std::make_unique<Orange>();
  return 0;
};

Опять же, мы можем ожидать, что результат будет таким:

virtual Orange::~Orange()
Data::~Data()
virtual Fruit::~Fruit()

Однако, если соберем и запустим этот код, то получим:

Base::~Base()

Никакого вывода ни из деструктора наследника, ни тем более из деструктора динамически выделенного объекта!
Как можно легко догадаться, в данной ситуации у нас будет утечка памяти, поскольку в конструкторе наследника была выделена память для объекта класса Data, а деструктор базового класса Fruit не помечен как virtual.
Данную ситуацию можно легко исправить, пометив деструктор наследуемого класса соответствующим образом:

virtual ~Orange() virtual
{
  std::cout << __PRETTY_FUNCTION__ << std::endl;
  delete data;
};

В результате при компиляции получим ошибку:

02_override.cpp:30:11: error: ‘virtual Orange::~Orange()’ marked ‘override’, but does not override
   virtual ~Orange() override
           ^

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