Наследование, построение иерархии, множественное наследование и неоднозначности в нём. Доминирование. - Painted-Black/BMSTU-OOP GitHub Wiki

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

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

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

Если вы пишете класс, который впоследствии будет использоваться как базовый класс при наследовании, то данные, к которым нужно будет иметь доступ, следует объявлять как protected. Заметим также, что наследование не работает в обратном направлении. Базовому классу и его объектам недоступны производные классы.

Схемы наследования:

  • private - базовый класс ничего незнает о производных. При этой схеме можно полностью сменить интерфейс. При его использовании для объектов производного класса нет доступа к методам базового класса, объявленным как public.
  • protected - полная смена интерфейса, но интерфейс базового класса остался доступным для наследников производного.
  • public - все члены наследуются с их уровнем доступа. Ключевое слово public определяет, что объект производного класса может иметь доступ к методам базового класса, объявленным как public.

В производном классе можно восстановить уровень доступа членов базового класса.

В производном классе можно определить такой же метод как в базовом – одно имя, один список параметров, один тип возврата. Метод в производном классе будет доминировать над методом базового класса.

В каких случаях использовать наследование?

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

Выделение общей базы (в порядке приоритета): ВСЕГДА выделяется класс, если есть общая схема использования. Сходство между набором операций (над объектами разных классов выполняются одинаковые операции). Совершенно разный функционал и разное использование, но могут быть выделены операции, имеющие единую реализацию. Желательно выделять базу даже в том случае, когда объекты разных классов фигурируют вместе в «дискуссиях по проекту» (когда возможно в дальнейшем может проявиться общность классов).

Расщепление класса: два подмножества операций класса используются в разной манере (в разных областях программы, домена). Группы операций имеют несвязанную реализацию. Один и тот же класс фигурирует в разных, не связанных между собой частях – лучше этот класс разделить.

ООП использует рекурсивный дизайн – постепенное разворачивание программы от базовых классов к более специализированным.

Класс может быть производным не только от одного базового класса, а и от мно- гих. Этот случай называется множественным наследованием.

class A 
{ 
};

class В 
{
}; 

class C : public A, public В
{ 
}; 

В определенных ситуациях могут появиться некоторые проблемы, связанные со множественным наследованием. Здесь мы рассмотрим наиболее общую. Допустим, что в обоих базовых классах существуют методы с одинаковыми именами, а в производном классе метода с таким именем нет. Как в этом случае объект производного класса определит, какой из методов базовых классов выбрать? Одного имени метода недостаточно, поскольку компилятор не сможет вычислить, какой из двух методов имеется в виду. Проблема решается путем использования оператора разрешения, определяющего класс, в котором находится метод. ObjC.A::show(); направляет нас к версии метода show(), принадлежащей классу A, а objC.B::show(); направляет нас к методу, принадлежащему классу B, Б. Страуструп (см. прило- жение 3 «Библиография») называет это устранением неоднозначности. Но подобный код считается плохим тоном.

Другой вид неопределенности появляется, если мы создаем производный класс от двух базовых классов, которые, в свою очередь, являются производными одного класса. Это создает дерево наследования в форме ромба. Классы В и C являются производными класса A, а класс D является производным классов В и C. Трудности начинаются, когда объект класса D пытается воспользоваться методом класса A. В этом примере объект objD использует метод func(). Однако классы В и C содержат в себе копии метода func(), унаследованные от класса A. Компилятор не может решить, какой из методов использовать, и сообщает об ошибке.

Базовый класс может выполнять объединяющую функцию - обладать набором свойств, присущих объектам производных классам. Функционал общий, однако выполняется по разному (трамвай ездит только по рельсам, а автобус – не только). Методы должны реализовывать производные классы, а базовый задает только их интерфейс. Было решено, что указатель на элемент ЛЮБОГО класса преобразуется к указателю на элемент его базового; в базовом классе метод описывается через virtual – будут создаваться «таблицы соответствия». Жрёт время и ресурсы (нужно спуститься по таблице), но мы получаем свободу подменить одно понятие другим. НЕ ИДЕНТИЧНО доминированию – при доминировании могут вызываться методы базовых классов, а при виртуальности – методы производного.

override требует, чтобы метод подменял базовый (c С11) final означает, что производные классы больше этот метод не подменят.

Доминирование

  • Метод f() производного класса доминирует над методом f() базового класса. Для объекта класса B будет вызываться метод f() класса B: class A { public: f(); };

    class B : public A { public: f(); };

Прямая база - входит в производный класс только один раз.

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