Урок №154. Базовое наследование

  Юрий  | 

    | 

  Обновл. 24 Июн 2019  | 

 5803

 ǀ   3 

Теперь, когда мы уже поговорили о наследовании в абстрактном ключе, давайте поговорим о том, как это используется в C++ на практике.

Наследование в С++

Наследование в C++ происходит между классами и имеет тип отношений «является». Класс, от которого наследуют, называется родительским (или ещё «базовым» или «суперклассом»), а класс, который наследует, называется дочерним (или ещё «производным» или «подклассом»).

В диаграмме выше Фрукт является родительским классом, а Яблоко и Банан — дочерними.

В диаграмме выше Треугольник является дочерним классом (родитель — Фигура) и родительским (для Правильного треугольника) одновременно.

Дочерний класс наследует как поведение (методы), так и свойства (переменные-члены) от родителя (с учётом некоторых ограничений доступа, которые мы рассмотрим в следующем уроке). Эти методы и переменные становятся членами дочернего класса.

Поскольку дочерние классы являются полноценными классами, то они могут (конечно) иметь и свои собственные члены. Сейчас мы это всё рассмотрим подробнее.

Класс Human


Вот простой класс Human для представления Человека:

В этом классе мы определили только те члены, которые являются общими для всех объектов этого класса. Каждый Человек (независимо от пола, профессии и т.д.) имеет Имя и Возраст.

Обратите внимание, в примере выше мы сделали все переменные-члены и методы класса открытыми. Это сделано ради простоты примера. Обычно переменные-члены нужно делать private. О средствах контроля доступа и о том, как это работает в наследовании, мы поговорим в следующих уроках этой главы.

Класс BasketballPlayer

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

Вот наш незавершенный класс BasketballPlayer:

Также нам нужно знать Имя и Возраст баскетболиста, а эта информация уже у нас есть: она хранится в классе Human.

У нас есть три варианта добавления Имени и Возраста в BasketballPlayer:

   Добавить Имя и Возраст в класс BasketballPlayer непосредственно в качестве членов. Это худший вариант, так как произойдёт дублирование кода, который уже существует в классе Human. Любые обновления в Human также должны быть продублированы и в BasketballPlayer.

   Добавить класс Human в качестве члена в класс BasketballPlayer, используя композицию. Но следует вопрос: «Может ли BasketballPlayer иметь Human?». Нет, это некорректно.

   Сделать так, чтобы BasketballPlayer унаследовал необходимые атрибуты от Human. Помните, что тип отношений в наследовании — «является». Является ли BasketballPlayer Human-ом (т.е. Человеком)? Конечно! Поэтому наш выбор — наследование.

Делаем класс BasketballPlayer дочерним


Чтобы класс BasketballPlayer унаследовал информацию от класса Human, нам нужно после объявления BasketballPlayer (class BasketballPlayer) использовать двоеточие, ключевое слово public и имя класса, от которого мы хотим унаследовать. Это называется открытым наследованием:

Проиллюстрируем:

Когда BasketballPlayer наследует свойства класса Human, то BasketballPlayer приобретает методы и переменные-члены класса Human. Кроме того, BasketballPlayer имеет ещё два своих собственных члена: m_gameAverage и m_points. Здесь есть смысл, так как эти свойства специфичны только для BasketballPlayer, а не для каждого Human-а.

Таким образом, объекты BasketballPlayer будут иметь 4 члена:

   m_ gameAverage и m_ points от BasketballPlayer;

   m_name и m_age от Human.

Полный код программы:

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

Anton

Это работает, так как anton является объектом класса BasketballPlayer, а все объекты класса BasketballPlayer имеют переменную-член m_name и метод getName(), унаследованные от класса Human.

Дочерний класс Employee

Теперь напишем ещё один класс, который также будет наследовать свойства Human. Например, класс Employee (Работник). Работник «является» Человеком, поэтому использовать наследование здесь уместно:

Работник наследует m_name и m_age от Human-а (а также два метода) и имеет ещё две собственные переменные-члены и один метод. Обратите внимание, метод printNameAndWage() использует переменные как из класса, к которому принадлежит (Employee::m_wage), так и с родительского класса (Human::m_name).

Проиллюстрируем:

Обратите внимание, Employee и BasketballPlayer не имеют прямых отношений, хотя оба наследуют свойства класса Human.

Вот полный код:

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

Ivan: 350

Цепочка наследований


Можно наследовать от класса, который сам наследует от другого класса. При этом ничего примечательного или чего-нибудь особенного не происходит — всё аналогично тому, что мы рассмотрели выше. Например, напишем класс Supervisor. Супервайзер — это Работник, который «является» Человеком. Мы уже написали класс Employee, поэтому будем его использовать в качестве родительского класса:

Смотрим:

Все объекты Supervisor наследуют методы и переменные от Employee и Human, а также имеют свою собственную переменную-член m_nOverseesID.

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

Почему наследование является полезным?

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

Например, если мы добавим новый метод в Human, то Employee и Supervisor автоматически получат доступ к нему. Если мы добавим новую переменную в Employee, Supervisor также получит доступ к ней. Это позволяет создавать новые классы более простым, интуитивно-понятным способом!

Заключение

Наследование позволяет повторно использовать классы путём наследования членов этих классов другими классами. В следующих уроках мы будем разбираться детальнее, как это всё работает.

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

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

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

  1. Аватар kmish:

    Спасибо! Все понятно, кроме одного 🙂
    Почему на всех схемах стрелки направлены к Родителю? Ведь Наследование распространяется в противоположном направлении. Или эти стрелки что-то другое нам говорят?

  2. Аватар dshadov:

    Добрый день!
    А почему у Вас в конструкторе по умолчанию Employee у переменной wage стоит тип int, а значение присваивается double 0.0?

    1. Юрий Юрий:

      Привет.
      Опечатка. Не знаю, как пропустил, но, спасибо, исправил.

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

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