Безопасное переопределение виртуальных функций С++

У меня есть базовый класс с виртуальной функцией, и я хочу переопределить эту функцию в производном классе. Есть ли способ заставить компилятор проверить, действительно ли функция, объявленная в производном классе, переопределяет функцию в базовом классе? Я хотел бы добавить макрос или что-то, что гарантирует, что я случайно не объявил новую функцию вместо того, чтобы переопределять старый.

Возьмем этот пример:

class parent {
public:
  virtual void handle_event(int something) const {
    // boring default code
  }
};

class child : public parent {
public:
  virtual void handle_event(int something) {
    // new exciting code
  }
};

int main() {
  parent *p = new child();
  p->handle_event(1);
}

Здесь parent::handle_event() вызывается вместо child::handle_event(), потому что дочерний метод пропускает объявление const и поэтому объявляет новый метод. Это также может быть опечаткой в ​​имени функции или некоторой незначительной разницей в типах параметров. Это также легко произойдет, если интерфейс базового класса изменится и где-то какой-то производный класс не был обновлен, чтобы отразить это изменение.

Есть ли способ избежать этой проблемы, могу ли я как-то сказать компилятору или другому инструменту проверить это для меня? Любые полезные флагов компилятора (желательно для g++)? Как избежать этих проблем?

Ответ 1

Начиная с g++ 4.7 он понимает новое ключевое слово С++ 11 override:

class child : public parent {
    public:
      // force handle_event to override a existing function in parent
      // error out if the function with the correct signature does not exist
      void handle_event(int something) override;
};

Ответ 2

Что-то вроде ключевого слова С# override не является частью С++.

В gcc -Woverloaded-virtual предостерегает от скрытия виртуальной функции базового класса с функцией с тем же именем, но с достаточно различной сигнатурой, которая не отменяет ее. Тем не менее, это не защитит вас от невозможности переопределить функцию из-за неправильного написания имени функции.

Ответ 3

Насколько я знаю, разве вы не можете просто сделать его абстрактным?

class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

Мне показалось, что я читал на www.parashift.com, что вы действительно можете реализовать абстрактный метод. Что имеет смысл для меня лично, единственное, что он делает, это принудительные подклассы для его реализации, никто не сказал ничего о том, что ему не разрешено иметь реализацию.

Ответ 4

В MSVC вы можете использовать ключевое слово CLR override, даже если вы не компилируете для CLR.

В g++ нет прямого способа обеспечить это во всех случаях; другие люди дали хорошие ответы о том, как поймать различия подписи, используя -Woverloaded-virtual. В будущей версии кто-то может добавить синтаксис типа __attribute__ ((override)) или эквивалент, используя синтаксис С++ 0x.

Ответ 5

В MSVC++ вы можете использовать ключевое слово override

class child : public parent {
public:
  virtual void handle_event(int something) <b>override</b> {
    // new exciting code
  }
};

override работает как для собственного кода, так и для кода CLR в MSVC++.

Ответ 6

Сделайте функцию абстрактной, чтобы производные классы не имели другого выбора, кроме как переопределить ее.

@Ray Недопустимый код.

class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

Абстрактные функции не могут иметь тела, определенные в строке. Он должен быть изменен, чтобы стать

class parent {
public:
  virtual void handle_event(int something) const = 0;
};

void parent::handle_event( int something ) { /* do w/e you want here. */ }

Ответ 7

Я бы предложил небольшое изменение в вашей логике. Он может работать или не работать, в зависимости от того, что вам нужно выполнить.

handle_event() все еще может выполнять "скучный код по умолчанию", но вместо того, чтобы быть виртуальным, в точке, где вы хотите, чтобы он выполнял "новый захватывающий код", базовый класс вызывает абстрактный метод (т.е. переопределенный) метод, который будет предоставлен вашим классом-потомком.

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

Ответ 8

У вашего компилятора может быть предупреждение, что он может генерировать, если функция базового класса становится скрытой. Если это так, включите его. Это вызовет столкновения const и различия в списках параметров. К сожалению, это не обнаружит орфографическую ошибку.

Например, это предупреждение C4263 в Microsoft Visual С++.

Ответ 9

Ключевое слово С++ 11 override при использовании с объявлением функции внутри производного класса вынуждает компилятор проверять, что объявленная функция фактически переопределяет некоторую функцию базового класса. В противном случае компилятор выдаст ошибку.

Следовательно, вы можете использовать спецификатор override для обеспечения динамического полиморфизма (переопределения функций).

class derived: public base{
public:
  virtual void func_name(int var_name) override {
    // statement
  }
};