Безопасное переопределение виртуальных функций 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;
}