Наследование
Наследование - это механизм, который позволяет создавать новые классы на основе уже существующих.
Новый класс, называемый подклассом или производным классом, может наследовать поля и методы от другого класса, называемого базовым классом или суперклассом.
Зачем это нужно?
Основная задача механизма наследования - избежать дублирования кода. Взглянем на пример:
Класс для создания котиков:Вот мы написали два класса Cat и Bird, а чем они отличаются?
Почти ничем, кроме того, что птица ещё умеет летать, а коту мы можем добавить ещё какие-то методы, например орать под окнами.
Кроме котов и птиц, мы можем создать приложение зоопарк, где у нас будут десятки животных,
у всех животных будут примерно одинаковые поля и методы.
В итоге, описывая каждого животного, нам придётся написать довольно много повторяющегося кода...
А что если мы создадим общий класс Animal,
запишем в нём основные поля и методы, которые подходят для всех животных. В классах животных укажем лишь то, чем они отличаются.
В итоге мы получим родительский класс Animal и классы животных которые от него наследуются, т.е. имеют те же поля и методы, что и родительский класс.
-
Animal
- базовый класс, содержащий общие для всех животных свойства -
Поля объявлены как
protected
, чтобы они были доступны в производных классах -
Конструктор инициализирует основные свойства животного
-
Метод
run()
реализует общее поведение для всех животных
Создание производных классов
Класс Cat (наследует Animal)-
Класс
Cat
наследует все поля и методы классаAnimal
-
Конструктор передает параметры в конструктор базового класса через
: Animal(...)
-
Добавлен новый метод
purr()
, специфичный только для кошек
-
Метод
run()
переопределен с помощьюoverride
для изменения поведения -
Добавлен новый метод
fly()
, которого нет в базовом классе -
Все остальные методы и поля остаются такими же, как у
Animal
Использование наследованных классов
-
Объекты производных классов могут использовать методы базового класса
-
Переопределенные методы заменяют реализацию базового класса
-
Новые методы добавляют уникальное поведение
Расширение функциональности (пример с базой данных)
В контексте программирования более правильно говорить, что дочерний класс расширяет родительский класс.
Т.е. добавляет родительскому классу функциональности.
Однако, в русскоговорящем обществе, больше прижился термин наследование.
Предположим, что мы используем библиотку для работы с базой данных DataBase, и там имеется класс, который умеет посылать основные запросы к БД.
Пример:-
MyDataBase
наследует всю функциональностьDataBase
-
Добавлен новый метод
deleteRowById()
-
Метод
getRowById()
переопределен для добавления логирования перед вызовом оригинального метода
Множественное наследование
-
Класс может наследовать от нескольких базовых классов
-
Важно избегать "ромбовидного" наследования без виртуальных базовых классов
-
Может приводить к неоднозначности (если одинаковые имена в разных базовых классах)
Практические рекомендации
-
Используйте public наследование для отношения "является" (is-a)
-
Избегайте глубоких иерархий наследования (не более 3-4 уровней)
-
Применяйте override для явного указания переопределения методов
-
Делайте деструкторы виртуальными в базовых классах
-
Используйте protected для членов, которые должны быть доступны в производных классах
-
Рассмотрите композицию как альтернативу наследованию для отношения "имеет" (has-a)
Наследование в C++ позволяет создавать иерархии классов, где:
-
Базовые классы содержат общую функциональность
-
Производные классы добавляют или изменяют поведение
-
Код становится более организованным и удобным для поддержки
-
Обеспечивается возможность повторного использования кода
Правильное применение наследования делает программу:
-
Более модульной
-
Легче расширяемой
-
Проще для понимания
-
Менее подверженной ошибкам