Перейти к содержимому

Наследование конструкторы

Конструкторы и наследование

В иерархии классов допускается, чтобы у базовых и производных классов были свои собственные конструкторы. В связи с этим возникает следующий резонный вопрос: какой конструктор отвечает за построение объекта производного класса: конструктор базового класса, конструктор производного класса или же оба? На этот вопрос можно ответить так: конструктор базового класса конструирует базовую часть объекта, а конструктор производного класса — производную часть этого объекта. И в этом есть своя логика, поскольку базовому классу неизвестны и недоступны любые элементы производного класса, а значит, их конструирование должно происходить раздельно.

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

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

С помощью формы расширенного объявления конструктора производного класса и ключевого слова base в производном классе может быть вызван конструктор, определенный в его базовом классе. Ниже приведена общая форма этого расширенного объявления:

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

Давайте рассмотрим пример:

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

А теперь рассмотрим вкратце основные принципы действия ключевого слова base. Когда в производном классе указывается ключевое слово base, вызывается конструктор из его непосредственного базового класса. Следовательно, ключевое слово base всегда обращается к базовому классу, стоящему в иерархии непосредственно над вызывающим классом. Это справедливо даже для многоуровневой иерархии классов. Аргументы передаются базовому конструктору в качестве аргументов метода base(). Если же ключевое слово отсутствует, то автоматически вызывается конструктор, используемый в базовом классе по умолчанию.

Наследование классов в C++ — урок 13

Создание базового класса

Для решения этой задачи создадим базовый класс human , который будет описывать модель человека. В нем будут храниться имя, фамилия и отчество.

Создайте файл human.h :

Наследование от базового класса

Теперь создайте новый класс student , который будет наследником класса human . Поместите его в файл student.h .

Функция get_average_score вычисляет среднее арифметическое всех оценок студента. Все публичные свойства и методы класса human будут доступны в классе student .

Конструктор базового класса

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

Читайте так же:  Можно ли вернуть деньги за авиабилеты

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

Список оценок студента хранится в векторе.

Создание объекта класса student

Реализуем пользовательский интерфейс для работы с классом student .

В этом примере мы написали программу, которая создает объект класса student , сохраняя в нем его имя, фамилию, отчество и список оценок.

После инициализации объекта, происходит вывод полного имени студента с помощью функции get_full_name . Эта функция была унаследована от базового класса human .

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

Мы реализовали часть функционала для нашей базы данных института (я конечно утрирую, когда оперирую столь серьезными высказываниями про настоящую базу данных 🙂

Создание класса-наследника teacher

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

Создайте файл teacher.h :

У класса teacher появилось новое свойство — количество учебных часов, отведенное преподавателю на единицу времени (семестр). Весь остальной функционал наследуется от базового класса human . Если бы мы писали все с нуля, то одинакового кода бы получилось в разы больше, и его поддержка усложнилась бы на порядок.

Создание объекта класса teacher

Изменим содержимое файла main.cpp , чтобы проверить работу класса teacher .

Если сборка программы прошла без ошибок, то результат работы программы будет таким:

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

Также, мы можем создать класс, который будет описывыть студента заочной формы обучения. Его мы унаследовали бы от класса student , добавив какие-либо дополнительные данные.

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

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

Когда нужно использовать конструктор

Если у класса много свойств — их совсем не обязательно задавать в конструкторе. Для сохранения отдельных свойств класса используют set-функции. Например, для сохранения номера паспорта, можно создать публичный метод set_passport_number(std::string number) , который будет принимать значение свойства и сохранять его в объекте, через переменную this .

Наследование конструкторов

Почему этот код:

Результат этих ошибок:

Не следует ли B наследовать конструктор A?

Читайте так же:  Договор соглашение при разделе имущества

В С++ 03 стандартные конструкторы не могут быть наследованы, и вам необходимо наследовать их вручную один за другим, вызывая базовую реализацию самостоятельно. Если ваш компилятор поддерживает стандарт С++ 11, существует наследование конструктора. Подробнее см. Статья в Википедии С++ 11. С новым стандартом вы пишете:

Конструкторы не наследуются. Они называются неявным или явным образом дочерним конструктором.

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

UPDATE. В С++ 11 конструкторы могут быть наследованы. См. Ответ Suma для деталей.

Наследование конструкторы

Есть базовый класс (библиотечный) у которого 8 конструкторов, с кучей параметров.
Мне нужно создать производный класс, в котором не будет ничего кроме одного единственного общедоступного поля. Неужели мне нужно переопределять все 8 конструкторов в производном классе, только для того чтобы моджно было динамически создать объект производного класса. Какой тогда смысл в концепции наследования. А если разработчик базового класса добавит девятый конструктор.

Что тут можно сделать?

Здравствуйте, Аноним, Вы писали:

А>Есть базовый класс (библиотечный) у которого 8 конструкторов, с кучей параметров.
А>Мне нужно создать производный класс, в котором не будет ничего кроме одного единственного общедоступного поля. Неужели мне нужно переопределять все 8 конструкторов в производном классе, только для того чтобы моджно было динамически создать объект производного класса. Какой тогда смысл в концепции наследования. А если разработчик базового класса добавит девятый конструктор.

Да, придется.
В следующем стандарте это поправят, можно будет отфорвардить конструкторы базы автоматически.

Здравствуйте, Аноним, Вы писали:

А>Есть базовый класс (библиотечный) у которого 8 конструкторов, с кучей параметров.
А>Мне нужно создать производный класс, в котором не будет ничего кроме одного единственного общедоступного поля. Неужели мне нужно переопределять все 8 конструкторов в производном классе, только для того чтобы моджно было динамически создать объект производного класса. Какой тогда смысл в концепции наследования. А если разработчик базового класса добавит девятый конструктор.
А>

А>Что тут можно сделать?

У Bas'a нет конструктора без параметров, поэтому в Derive необходимо корректно вызвать конструктор Base.

class Derive : public Base
<
public:
Derive():Base(1,2,3,4)<>
int v;
>;

Наследование конструкторы

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

Мне подсказали, что, если параметры одинаковые, то конструктор в наследнике можно не писать или что-то типа того.

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

Короче говоря, как должен быть написан этот код правильно?

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

Читайте так же:  Заявление о вымогательстве банк

Если из класса Employee уберем определение конструктора:

В данном случае мы получим ошибку, так как класс Employee не соответствует классу Person , а именно не вызывает конструктор базового класса. Даже если бы мы добавили какой-нибудь конструктор, который бы устанавливал все те же свойства, то мы все равно бы получили ошибку:

То есть в классе Employee через ключевое слово base надо явным образом вызвать конструктор класса Person :
Либо в качестве альтернативы мы могли бы определить в базовом классе конструктор без параметров:

Вот полная статья о Наследовании

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

Наследование конструкторы

Вот это у меня написано в .h-файле

А вот это в .cpp (то что в теле конструкторов это инициализация полей Category )

Если в определении ничего про базовый конструктор не писать, то компилятор в принципе и не ругается. Как надо-то? В инете полно инфы как их наследовать, но не могу найти про вынесение всего этого за пределы класса. Тоже самое с конструктором копирования. И как быть с перегруженными операторами(например присваивания), их виртуальными просто сделать?

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

Странное и неверное заявление. Базовый конструктор тут вообще ни при чем. В заголовочный файл для класса Category вы поместили инициализацию базового класса и тела конструкторов в виде <> . В файл реализации вы снова поместили инициализацию базового класса и еще одни тела ваших конструкторов в виде < что-то >. Этим нарушено Правило Одного Определения. У одной и той же функции не может быть два тела. Зачем вы два раза определяете тело для каждого конструктора?

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

Ваши определения конструкторов в файле реализации выглядят нормально. Зачем вы тогда написали инициализацию базового класса и какие-то <> в заголовочном файле?

И как быть с перегруженными операторами(например присваивания), их виртуальными просто сделать?

Здесь вообще непонятно о чем идет речь. При чем здесь виртуальность вообще?

Конструкторы и деструкторы производных классов

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

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

#include
class Base <
public:
Base () class Base <
public:
Base() class Base <
public:
Base()

Для любых предложений по сайту: [email protected]