Безопасное переопределение виртуальных функций С++
У меня есть базовый класс с виртуальной функцией, и я хочу переопределить эту функцию в производном классе. Есть ли способ заставить компилятор проверить, действительно ли функция, объявленная в производном классе, переопределяет функцию в базовом классе? Я хотел бы добавить макрос или что-то, что гарантирует, что я случайно не объявил новую функцию вместо того, чтобы переопределять старый.
Возьмем этот пример:
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
}
};