2009-01-30 3 views
92

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

+2

Большой вопрос, я пытался понять, почему моя функция класса ребенок Безразлично, т дозвонились, так как через час сейчас! –

ответ

78

Поскольку г ++ 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; 
}; 
+0

Большое спасибо за понимание. –

+0

@hirschhornsalz: Я обнаружил, что когда вы реализуете функцию handle_event и добавляете переопределение в конце реализации функции, g ++ дает ошибку; если вы предоставляете встроенную функцию в объявлении класса после ключевого слова override, все в порядке. Зачем? – h9uest

+3

@ h9uest 'override' должно использоваться в определении. Встроенная реализация - это определение и реализация, так что это нормально. – hirschhornsalz

3

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

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

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

2

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

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

20

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

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

+2

Это если вы используете Visual C++ –

+4

Использование Visual C++ не делает 'override' ключевым словом в C++; это может означать, что вы используете что-то, что может скомпилировать некоторый недопустимый исходный код C++. ;) –

+3

Тот факт, что переопределение недопустимо, C++ означает, что стандарт неверен, а не Visual C++ – Jon

15

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

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

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

+0

Только теперь я понял, что это работает не только для деструкторов! Отличная находка. – strager

+1

Есть пара потенциальных недостатков: 1) другое, что маркировка одного или нескольких методов в качестве абстрактного делает, делает базовый класс недействительным, что может быть проблемой, если это не является частью предполагаемого использования класса. 2) базовый класс не может быть изменен в первую очередь. –

+3

Я согласен с Майклом Берром. Создание абстрактного абстрактного элемента не является частью вопроса.Совершенно разумно иметь базовый класс с функциональностью в виртуальном методе, который вы хотите переопределить производный класс. И так же разумно хотеть защитить от другого программиста, переименовавшего функцию в базовый класс, и заставить ваш производный класс больше не переопределять его. Расширение Microsoft «override» неоценимо в этом случае. Я бы хотел, чтобы это было добавлено к стандарту, потому что, к сожалению, нет хорошего способа сделать это без него. – Brian

11

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

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

5

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

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

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

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

override работает как для нативной и CLR кода в MSVC++.