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

Наследование и структура в С++Наследование — одна из важнейших особенностей объектно-ориентированного программирования.

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

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

Зачем нужно наследование?

Рассмотрим группу транспортных средств, таких как: автомобиль, автобус, джип. Каждое из этих транспортных средств будет иметь свойства и методы, они показаны на диаграмме ниже:

Диаграмма наследования

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

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

предотвратить дублирование кода

На приведенной выше картинке мы определили базовый класс «Vehicles» (транспортные средства) и производные от него классы «Car» (автомобиль), «Bus» (автобус) и «Jeep» (джип). Общие методы и свойства теперь являются частью класса Vehicles. Поскольку другие классы являются производными от класса Vehicles, все классы приобретают эти методы и свойства.

Следовательно, нам просто нужно написать общий код только один раз и все три класса; автомобиль, автобус и джип приобретут его.

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

Общий формат наследования класса:

class derived_classname: access_specifier base_classname
{
};

В этом формате, « derived_classname » — это имя производного класса, « access_specifier » — это режим доступа, т.е. общедоступный, защищенный или частный, в котором производный класс должен наследовать базовый класс, а « derived_classname » — это имя базового класса, из которого и наследует производный класс.

Способы наследования

«access_specifier», показанный в приведенном выше объявлении наследования, может иметь свои значения, как показано на картинке ниже:

access_specifier

В зависимости от access_specifier, указанного при наследовании класса, есть различные режимы наследования, они перечислены ниже:

Публичное наследование (public)

Общий синтаксис:

class sub_class : public parent_class

Когда указан спецификатор открытого доступа, открытые члены базового класса наследуются как общедоступные, а защищенные члены защищены. Частные члены остаются частными. Это самый популярный способ наследования.

Читать также:  Директивы препроцессора в C++

Частное наследование (private)

Общий синтаксис:

class sub_class : parent_class

Частное наследование ничего не наследует. Когда используется спецификатор доступа private, общедоступные и защищенные члены базового класса также становятся частными.

Защищенное наследование (protected)

Общий синтаксис:

class sub_class:protected parent_class

Когда используется спецификатор защищенного доступа, общедоступные и защищенные члены базового класса становятся защищенными членами производного класса.

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

Ниже приведено табличное представление всех режимов доступа и их интерпретация для наследования:

Производный класс ->
Базовый класс Частный Публичный (общественный) Защищенный
Частный Не унаследованный Не унаследованный Не унаследованный
Общественный Частный Общественный Защищенный
Защищенный Частный Защищенный Защищенный

Порядок конструкторов/деструкторов в наследовании

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

Следующая программа продемонстрирует порядок наследования конструкторов. У нас есть базовый класс «Base», который имеет конструктор по умолчанию и конструктор с параметрами. Мы получаем от него класс под названием «Derived» (производный), который также имеет один конструктор по умолчанию и другой параметризованный конструктор.

Вывод данных этой программы показывает порядок, в котором вызываются конструкторы:

Вывод данных:

Base class default constructor
Base class default constructor
Derived class default constructor
Base class parameterized constructor
Derived class parameterized constructor

Становится видно, что после создания объекта базового класса мы создаем объект производного класса с конструктором по умолчанию. При создании этого объекта сначала вызывается конструктор базового класса по умолчанию, а затем выполняется конструктор производного класса.

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

Обратите внимание, что если бы в базовом классе не было параметризованного конструктора, то конструктор по умолчанию вызывался бы даже для создания параметризованного объекта производного класса.

Но остается вопрос, почему конструктор базового класса вызывается при создании объектов производного класса?

Вы уже знаете, что конструктор используется для создания объектов класса, а также для инициализации членов класса. Когда создается объект производного класса, его конструктор имеет контроль только над членами производного класса.

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

Читать также:  Преобразование типов в C++

В результате весь объект не будет создан эффективно. По этой причине все конструкторы базового класса вызываются первыми при создании объекта производного класса.

Типы наследования

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

Виды наследования

Наследование шаблонов

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

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

Вывод данных:

basecls_Template<int> obj = 100
derivedcls_Child obj1(inherited from basecls_Template<char> = A

В приведенной выше программе у нас есть шаблон с именем basecls_Template, который определяет шаблон класса для базового класса. Затем мы определяем класс, производный cls_Child, который мы хотим получить из класса шаблона.

Но обратите внимание, что класс basecls_Template — это только тип, а не класс. Следовательно, мы не можем вывести класс derivedcls_Child из этого шаблона.

Поэтому, если мы объявим дочерний класс как:

class derivedcls_Child : public basecls_Template

Это приведет к ошибке. Причина в том, что basecls_Template — это тип данных, а не класс. Таким образом, для того, чтобы наследовать элементы basecls_Template, мы должны сначала создать его экземпляр, прежде чем наследовать от него.

Поэтому приведенный выше оператор Class derivedcls_Child : public basecls_Template<char> работает нормально.

В этом операторе мы преобразовали шаблон basecls_Template в шаблон класса символов. После того, как мы воспользуемся этим инстанцированным классом-шаблоном, все последующие действия, такие как создание и использование объектов, совпадут с обычной работой наследования.

Структура

Наследование в основном изображает вид отношений, в которых отношение указывает на часть. Например, Змея — это разновидность Рептилии. Мы также можем сказать, что Рептилия является частью класса Животных.

В заключение, наследование указывает на тип отношений «IS-A» , где мы можем сказать, что производный класс является частью базового класса.

Мы также можем представить отношения в целом. Например, если мы говорим, что класс Зарплата является частью класса Сотрудник, то мы не представляем его должным образом. Мы знаем, что у сотрудников есть зарплата. Таким образом, удобнее говорить «Зарплата сотрудника».

Точно так же, если мы возьмем в качестве примера класс транспортных средств, мы можем сказать, что у транспортного средства есть двигатель или у транспортного средства есть шасси. Таким образом, все эти отношения изображают отношения «HAS-A» , которые представляют целый объект, содержащийся в другом классе. Это определяется как Структура.

Отношения, изображаемые структурой, зависят друг от друга. Например, шасси не может существовать без транспортного средства. Точно так же зарплата не может существовать без сотрудника.

Читать также:  Строки в C++ с примерами

Мы можем представить структуру схематически, она показано ниже:

Схема структуры

Структуру также называют сдерживанием. В приведенном выше представлении мы показали родительский класс. В отличие от наследования мы включаем объект дочернего класса в родительский класс. Это сдерживание или структура.

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

Вывод данных:

10001 Viktor
A-101 Svobodi Moscovskaya Moscow

В этом примере есть родительский класс Employee и дочерний класс Address. Внутри родительского класса Employee мы объявили указатель на класс Address, а также инициализировали этот объект в конструкторе Employee. Таким образом, мы изображаем отношение, согласно которому у сотрудника есть адрес, который является структурой.

Что выбрать, структуру или наследование?

И структура, и наследование отображают отношения между классами. В то время как наследование изображает отношения «IS-A», структура изображает отношения «HAS-A».

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

Оба способствуют повторному использованию кода. Наследование может сделать код громоздким по мере усложнения решений, но в то же время оно также позволяет нам расширять существующий код. Таким образом, мы должны использовать наследование, когда нам нужно изменить и использовать свойства и методы другого класса внутри нового класса.

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

Таким образом, лучшее решение о том, использовать структуру или наследование, будет принято путем взвешивания плюсов и минусов обоих методов для конкретной ситуации.

Итог

Итак, мы подошли к концу нашей статьи о наследовании. Мы вам показали различные способы наследования. Мы также показали типы наследования, которые мы рассмотрим в нашей следующей статье. Вы узнали о порядке конструкторов, которые выполняются в случае наследования.

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

Структура — это еще один тип отношения классов, и нам нужно сначала знать точную ситуацию, и только тогда мы сможем решить, что использовать, структуру или наследование.

В нашей следующей статье вы узнаем о типах наследования в C++.

С Уважением, МониторБанк

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