Вопрос 22: Иерархия классов. Производные классы — что это даёт. Функции-члены. Конструкторы и деструкторы. Пример иерархии классов.
Иерархия классов — это структура многочисленных связанных классов, определяющая, какие классы наследуют функции от других классов. Наследование функций порождается наследованием классов.
Наследование — это механизм создания нового класса на основе уже существующего. При этом к существующему классу могут быть добавлены новые элементы (данные и функции), либо существующие функции могут быть изменены. Основное назначение механизма наследования — повторное использование кодов, так как большинство используемых типов данных являются вариантами друг друга, и писать для каждого свой класс нецелесообразно. Объекты разных классов и сами классы могут находиться в отношении наследования, при котором формируется иерархия объектов, соответствующая заранее предусмотренной иерархии классов.
Иерархия классов позволяет определять новые классы на основе уже имеющихся. Имеющиеся классы обычно называют базовыми(иногда порождающими), а новые классы, формируемые на основе базовых, – производными (порожденными, классами-потомками или наследниками).
Производные классы «получают наследство» – данные и методы своих базовых классов, и могут пополняться собственными компонентами (данными и собственными методами). Наследуемые компоненты не перемещаются в производный класс, а остаются в базовых классах. Сообщение, обработку которого не могут выполнить методы производного класса, автоматически передается в базовый класс. Если для обработки сообщения нужны данные, отсутствующие в производном классе, то их пытаются отыскать автоматически в базовом классе.
При наследовании некоторые имена методов (функций-членов) и данных-членов базового класса могут быть по-новому определены в производном классе. В этом случае соответствующие компоненты базового класса становятся недоступными из производного класса. Для доступа из производного класса к компонентам базового класса, имена которых повторно определены в производном, используется операция разрешения контекста ::
Для порождения нового класса на основе существующего используется следующая общая форма
сlass Имя : МодификаторДоступа ИмяБазовогоКласса
{
объявление_членов;
};
Модификотор доступа может быть:
- public
- private
- protected
- отсутствовать => по умолчанию будет использоваться public
Спецификация доступа | внутри класса | в порожденном классе | вне класса |
---|---|---|---|
private | + | - | - |
protected | + | + | - |
public | + | + | + |
Общее наследование означает, что порожденный класс – это подтип базового класса. Таким образом, порожденный класс представляет собой модификацию базового класса, которая наследует общие и защищенные члены базового класса.
Указатель на порожденный класс может быть неявно передан в указатель на базовый класс. При этом переменная-указатель m на базовый класс может указывать на объекты как базового, так и порожденного класса. Указатель на порожденный класс может указывать только на объекты порожденного класса. Неявные преобразования между порожденным и базовым классами называются предопределенными стандартными преобразованиями:
- объект порожденного класса неявно преобразуется к объекту базового класса.
- ссылка на порожденный класс неявно преобразуется к ссылке на базовый класс.
- указатель на порожденный класс неявно преобразуется к указателю на базовый класс.
Частное наследование Порожденный класс может быть базовым для следующего порождения. При порождении private наследуемые члены базового класса, объявленные как protected и public, становятся членами порожденного класса с видимостью private. При этом члены базового класса с видимостью public и protected становятся недоступными для дальнейших порождений. Цель такого порождения — скрыть классы или элементы классов от использования в дальнейших порождениях. При порождении private не выполняются предопределенные стандартные преобразования.
Модификатор наследования → | public | protected | private |
---|---|---|---|
Модификатор доступа ↓ | |||
public | public | protected | private |
protected | protected | protected | private |
private | нет доступа | нет доступа | нет доступа |
Конструкторы и деструкторы при наследовании Как базовый, так и производный классы могут иметь конструкторы и деструкторы. Если и у базового и у производного классов есть конструкторы и деструкторы, то конструкторы выполняются в порядке наследования, а деструкторы – в обратном порядке. То есть если А – базовый класс, В – производный из А, а С – производный из В (А-В-С), то при создании объекта класса С вызов конструкторов будет иметь следующий порядок:
- конструктор класса А
- конструктор класса В
- конструктор класса С.
Вызов деструкторов при удалении этого объекта произойдет в обратном порядке:
- деструктор класса С
- деструктор класса В
- деструктор класса А.
Поскольку базовый класс "не знает" о существовании производного класса, любая инициализация выполняется в нем независимо от производного класса, и, возможно, становится основой для инициализации, выполняемой в производном классе. Поскольку базовый класс лежит в основе производного, вызов деструктора базового класса раньше деструктора производного класса привел бы к преждевременному разрушению производного класса.
Конструкторы могут иметь параметры. При реализации наследования допускается передача параметров для конструкторов производного и базового класса. Если параметрами обладает только конструктор производного класса, то аргументы передаются обычным способом. При необходимости передать аргумент из производного класса конструктору родительского класса используется расширенная запись конструктора производного класса:
КонструкторПроизводногоКласса (СписокФормальныхАргументов)
: КонструкторБазовогоКласса (СписокФактическихАргументов)
{
// тело конструктора производного класса
}
Для базового и производного классов допустимо использование одних и тех же аргументов. Возможно, списки аргументов конструкторов производного и базового классов будут различны. Конструктор производного класса не должен использовать все аргументы, часть предназначены для передачи в базовый класс:
student :: student(char *f, char *s, char *n) {
strcpy(fac, f);
strcpy(spec, s);
strcpy(name, n);
}
grad_student :: grad_student(char *f, char *s, char *n, char *w, int y) :
student(f,s,n) {
year = y;
strcpy(work, w);
}
Пример реализации
class student {
protected:
char fac[20];
char spec[30];
char name[15];
public:
student(char *f, char *s, char *n);
void print();
};
class grad_student : public student {
protected:
int year;
char work[30];
public:
grad_student(char *f, char *s, char *n, char *w, int y);
void print();
};