it-swarm.com.ru

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

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

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

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

97
sth

Начиная с 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;
};
84
Gunther Piez

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

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

20
CB Bailey

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

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

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

17
Ray Hidayat

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

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

11
Doug

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

 класс child: public parent {
 public: 
 virtual void handle_event (int что-то) переопределение {
 // новый захватывающий код 
} 
}; 

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

9
bobobobo

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

@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. */ }
5
Tanveer Badar

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

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

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

3
JMD

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

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

2
Mark Ransom