Стандарты программирования на С++. 101 правило и рекомендация | страница 67
Определение в производном классе перекрытия, которое может быть неуспешным (например, генерировать исключения; см. рекомендацию 70), будет корректно только в том случае, когда в базовом классе не объявлено, что данная операция всегда успешна. Например, скажем, класс >Employee
содержит виртуальную функцию-член >GetBuilding
, предназначение которой — вернуть код здания, в котором работает объект >Employee
. Но что если мы захотим написать производный класс >RemoteContractor
, который перекрывает функцию >GetBuilding
, в результате чего она может генерировать исключения или возвращать нулевой код здания? Такое поведение корректно только в том случае, если в документации класса >Employee
указано, что функция >GetBuilding
может завершаться неуспешно, и в классе >RemoteContractor
сообщение о неудаче выполняется документированным в классе >Employee
способом.
Никогда не изменяйте аргумент по умолчанию при перекрытии. Он не является частью сигнатуры функции, и клиентский код будет невольно передавать различные аргументы в функцию, в зависимости от того, какой узел иерархии обращается к ней. Рассмотрим следующий пример:
>class Base {
> // ...
> virtual void Foo(int x = 0);
>};
>class Derived : public Base {
> // ...
> virtual void Foo(int x = 1); // лучше так не делать...
>};
>Derived *pD = new Derived;
>pD->Foo(); // Вызов pD->Foo(1)
>Base *pB = pD;
>pB->Foo(); // вызов pB->Foo(0)
У некоторых может вызвать удивление, что одна и та же функция-член одного и того же объекта получает разные аргументы в зависимости от статического типа, посредством которого к ней выполняется обращение.
Желательно добавлять ключевое слово >virtual
при перекрытии функций, несмотря на его избыточность — это сделает код более удобным для чтения и понимания.
Не забывайте о том, что перекрытие может скрывать перегруженные функции из базового класса, например:
>class Base{ // ...
> virtual void Foo(int);
> virtual void Foo(int, int);
> void Foo(int, int, int);
>};
>class Derived : public Base { // ...
> virtual void Foo(int); // Перекрывает Base::Foo(int),
> // скрывая остальные функции
>};
>Derived d;
>d.Foo(1); // Все в порядке
>d.Foo(1, 2); // Ошибка
>d.Foo(1, 2, 3); // Ошибка
Если перегруженные функции из базового класса должны быть видимы, воспользуйтесь объявлением >using
для того, чтобы повторно объявить их в производном классе:
>class Derived : public Base { // ...