Вызов переопределенной функции из переопределяющей функции

Предположим, у меня есть виртуальная функция foo() в классе B, и мне нужно немного другое поведение в одном из производных классов B, классе D. Можно ли создать переопределяющую функцию D::foo() и вызвать B::foo( ) оттуда, после обработки особого случая? Как это:

void D::foo()
{
  if (/*something*/)
     // do something
  else
     B::foo();
}

Я не спрашиваю, сработает ли это, я знаю, что сработает. Я хочу знать, правильно ли это с точки зрения хорошего ООД.


person Igor Oks    schedule 13.05.2009    source источник


Ответы (7)


Это прекрасно. На самом деле, канонический способ выполнения некоторых операций — это вызвать метод базового класса, а затем сделать что угодно (или наоборот). Я думаю о operator= здесь. Конструкторы обычно работают таким же образом, даже если это немного замаскировано в списке инициализации.

person Gorpik    schedule 13.05.2009

Да, все в порядке, если вы не нарушаете принцип замещения Лискова.

person Alex Jenter    schedule 13.05.2009

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

person dirkgently    schedule 13.05.2009

Ничего страшного. Синтаксис, который вы указали, также можно использовать для временного отключения полиморфизма, т. е. когда вы вызываете метод obj->B::foo(), он будет выбран из класса B независимо от того, является ли foo() виртуальным или нет, и если obj является экземпляром B или нет (хотя это должен быть экземпляр класса, расширяющий B).

person Community    schedule 13.05.2009
comment
Вы не можете гарантировать, что функция не вызывается из подкласса B, и в этом случае вы вообще ничего не отключаете. - person xtofl; 13.05.2009
comment
@xtofl: Вы говорите, что если у меня B расширяет A, C расширяет B, то есть иерархию классов A‹B‹C, и C obj;, то вызов obj-›B::foo() разрешен для вызова A::foo () ? - person ; 13.05.2009
comment
@xtolf: я только что проверил свой ответ, и он кажется правильным. - person ; 13.05.2009

Да, это то, что ваш компилятор делает для вас каждый раз, когда он генерирует конструктор и деструктор: например, вызывает материнский. Я часто полагаюсь на этот «трюк» в своем собственном коде.

person yves Baumes    schedule 13.05.2009

Можно вызывать базовую реализацию, но выполнение этого условно отдаляет вас от семантики конструктора, вопреки тому, что было предложено в других ответах.

Вы можете столкнуться с проблемой хрупкого базового класса двумя способами:

  • Предполагая, что B::foo() обеспечивает общее поведение для всей иерархии (т.е. забывая, что метод не всегда вызывается)
  • Неприятные проблемы в зависимости от того, что на самом деле делает // do something!

Для полноты картины давайте упомянем симметричный подход к проектированию: Шаблон шаблона (базовая реализация вызывает конкретную подчасть)

person YvesgereY    schedule 17.12.2013

person    schedule
comment
Общий, стандартный, нормальный, типичный, широко используемый. Действительно, совершенно необходимо уметь это делать. - person S.Lott; 13.05.2009