Урок №159. Переопределение методов родительского класса

  Юрий  | 

  |

  Обновл. 15 Сен 2021  | 

 49103

 ǀ   9 

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

Вызов методов родительского класса

При вызове метода через объект дочернего класса, компилятор сначала смотрит, существует ли этот метод в дочернем классе. Если нет, то он начинает продвигаться по цепочке наследования вверх и проверяет, был ли этот метод определен в любом из родительских классов. Компилятор будет использовать первое найденное определение. Например:

Результат выполнения программы:

I am a Parent!
I am a Parent!

При вызове child.identify(), компилятор смотрит, определен ли метод identify() в классе Child. Нет, поэтому компилятор переходит к классу Parent. В классе Parent есть определение метода identify(), поэтому компилятор использует именно это определение.

Переопределение методов родительского класса


Однако, если бы мы определили метод identify() в классе Child, то использовалось бы именно это определение. Это означает, что мы можем заставить родительские методы работать по-другому с нашими дочерними классами, просто переопределяя их в дочерних классах!

Вышеприведенный пример станет лучше, если child.identify() будет выводить I am a Child!. Давайте изменим метод identify() в классе Child так, чтобы он возвращал правильный ответ.

Переопределение родительского метода в дочернем классе происходит, как обычное определение метода:

Вот тот же код main(), что и в примере, приведенном выше, но уже с внесенными изменениями в класс Child:

Результат:

I am a Parent!
I am a Child!

Обратите внимание, когда мы переопределяем родительский метод в дочернем классе, то дочерний метод не наследует спецификатор доступа родительского метода с тем же именем. Используется тот спецификатор доступа, который указан в дочернем классе. Таким образом, метод, определенный как private в родительском классе, может быть переопределен как public в дочернем классе, или наоборот!

Расширение функционала родительских методов

Могут быть случаи, когда нам не нужно полностью заменять метод родительского класса, но нужно просто расширить его функционал. Обратите внимание, в примере, приведенном выше, метод Child::identify() полностью перекрывает Parent::identify()! Возможно, это не то, что нам нужно. Мы можем вызвать метод родительского класса с тем же именем в методе дочернего класса (для повторного использования кода), а затем добавить дополнительно свой код.

Чтобы метод дочернего класса вызывал метод родительского класса с тем же именем, нужно просто выполнить обычный вызов функции, но с добавлением имени родительского класса и оператора разрешения области видимости. В следующем примере мы выполним переопределение identify() в классе Child, вызывая сначала Parent::identify(), а затем добавляя уже свой код:

Вместе с:

Дает результат:

I am a Parent!
I am a Parent!
I am a Child!

При выполнении child.identify() выполняется вызов Child::identify(). В Child::identify() мы сначала вызываем Parent::identify(), который выводит I am a Parent!. Когда Parent::identify() завершает свое выполнение, Child::identify() продолжает свое выполнение и выводит I am a Child!.

Всё просто. Зачем тогда нужно использовать оператор разрешения области видимости (::)? А затем, что, если бы мы определили Child::identify() следующим образом:

То вызов метода identify() без указания оператора разрешения области видимости привел бы к вызову identify() в текущем классе, т.е. Child::identify(). Затем снова вызов Child::identify(), и ура — у нас получился бесконечный цикл. Поэтому использование оператора разрешения области видимости является обязательным условием при изменении методов родительского класса.


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (240 оценок, среднее: 4,91 из 5)
Загрузка...

Комментариев: 9

  1. Алексей:

    Если в дочернем классе не вызывать переопрделенный метод напрямую, а вызвать его через другой метод родительского класса то будет вызван метод родительского класса:

    Результат STATE: 0(false).

    Получается, что если вызывать метод (setFlag) не напрямую, а через другой метод (doSomething) РОДИТЕЛЬСКОГО класса (не переопределенный в дочернем), то вызовется метод (setFlag) родительского класса, хотя объект владеет переопределенным методом (setFlag).

    Теперь мой вопрос: есть ли способ решить данную проблему не прибегая к виртуальным методам?

    1. Артём:

      Кажется, в этом случае наиболее правильным было бы просто переопределить doSomething в дочернем классе.

    2. Fantasy:

      Говорили же что он будет искать первое папвшееся решение у тебя ду самсинг вызывает сеттер и ближайший к нему оказывается сеттер из этого же класса…

    3. Fantasy:

      Это очевидно же? Ты же вызываешь ф-к унаследованую от родителя следовательно она выполняется в теле родителя а откуда родителю знать про его дочерние класы? На предыдущих уроках об этом рассказывали. Просто тебе нужно переопределить ф-к родительского класса!

  2. Снова я:

    У меня именно так и получилось в прошлом тестовом задании когда я решил что под каждый потомок класса Fruit заново перегружать оператор вывода << бредово и попытался вызвать << родительского класса для того чтобы вывести name и color и добавить к нему функционал вывода fiber.
    Но я так и не понял как вызвать родительский операторв вывода << . С обычной функцией и методом все просто . Вряд ли мне кто-то ответит (зайду завтра-послезавтра еще глянуть) , а пока полезу в интернет искать решение

    1. Пётр:

      Ответ для всех, кто также пытался это сделать (я, кстати, тоже)

  3. Александр:

    *** и ура — у нас получился бесконечный цикл

    у нас получилась рекурсия 🙂
    которая в С++ окажется весьма конечной, причем довольно быстро — вызов функций сожрет память на стеке и вылетит с ошибкой времени выполнения 🙂

    1. Surprizze:

      вызов рекурсии и есть цикл с определенным кол-вом вызовов(итераций)

      1. Александр:

        Не правда. Цикл и рекурсия — это все таки разные вещи

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

        Но в общем случае далеко не всегда рекурсия и цикл тождественны. Довольно часто для замены рекурсии на цикл нужно радикально менять математику

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *