Стандарты программирования на С++. 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 { // ...