Безопасное переопределение виртуальных функций 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++)? Как избежать этих проблем?

10 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

class derived: public base{
public:
  virtual void func_name(int var_name) override {
    // statement
  }
};
      class MyClass {
public:
 
  MyClass() {}
  virtual uint32_t someFunction(bool param = false) {
    if (param) {
      std::cout << "This is an example virtual function with default code" << std::endl;
    }
    return 1100;  //just for return something
  };

 
  • тогда вы можете переопределить функцию по мере необходимости
      class MyClass2 : public MyClass  {
public:
  MyClass2();
  uint32_t someFunction(bool param) override;
};

      
uint32_t MyClass2::someFunction(bool verbose) {
  std::cout << "This is new implementation for virtual method " << std::endl;

}

Другие вопросы по тегам