Безопасное переопределение виртуальных функций C ++ - PullRequest
97 голосов
/ 31 января 2009

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

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

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 ++)? Как избежать этих проблем?

Ответы [ 8 ]

84 голосов
/ 23 июля 2012

Начиная с g ++ 4.7, оно понимает новое ключевое слово C ++ 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;
};
20 голосов
/ 31 января 2009

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

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

17 голосов
/ 31 января 2009

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

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

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

11 голосов
/ 31 января 2009

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

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

9 голосов
/ 16 июля 2011

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

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

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

5 голосов
/ 31 января 2009

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

@ 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. */ }
3 голосов
/ 31 января 2009

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

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

РЕДАКТИРОВАТЬ: И если позже вы решите, что некоторые из ваших классов-потомков не нуждаются в предоставлении "нового захватывающего кода", то вы можете изменить реферат на виртуальный и предоставить пустую реализацию этого базового класса " вставлена ​​"функциональность.

2 голосов
/ 31 января 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...