Вопрос 22: Иерархия классов. Производные классы — что это даёт. Функции-члены. Конструкторы и деструкторы. Пример иерархии классов.

Иерархия классов — это структура многочисленных связанных классов, определяющая, какие классы наследуют функции от других классов. Наследование функций порождается наследованием классов.

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

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

Производные классы «получают наследство» – данные и методы своих базовых классов, и могут пополняться собственными компонентами (данными и собственными методами). Наследуемые компоненты не перемещаются в производный класс, а остаются в базовых классах. Сообщение, обработку которого не могут выполнить методы производного класса, автоматически передается в базовый класс. Если для обработки сообщения нужны данные, отсутствующие в производном классе, то их пытаются отыскать автоматически в базовом классе.

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

Для порождения нового класса на основе существующего используется следующая общая форма

сlass Имя : МодификаторДоступа  ИмяБазовогоКласса
{ 
    объявление_членов;
};

Модификотор доступа может быть:

  1. public
  2. private
  3. protected
  4. отсутствовать => по умолчанию будет использоваться public
Спецификация доступа внутри класса в порожденном классе вне класса
private + - -
protected + + -
public + + +

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

Указатель на порожденный класс может быть неявно передан в указатель на базовый класс. При этом переменная-указатель m на базовый класс может указывать на объекты как базового, так и порожденного класса. Указатель на порожденный класс может указывать только на объекты порожденного класса. Неявные преобразования между порожденным и базовым классами называются предопределенными стандартными преобразованиями:

  • объект порожденного класса неявно преобразуется к объекту базового класса.
  • ссылка на порожденный класс неявно преобразуется к ссылке на базовый класс.
  • указатель на порожденный класс неявно преобразуется к указателю на базовый класс.

Частное наследование Порожденный класс может быть базовым для следующего порождения. При порождении private наследуемые члены базового класса, объявленные как protected и public, становятся членами порожденного класса с видимостью private. При этом члены базового класса с видимостью public и protected становятся недоступными для дальнейших порождений. Цель такого порождения — скрыть классы или элементы классов от использования в дальнейших порождениях. При порождении private не выполняются предопределенные стандартные преобразования.

Модификатор наследования → public protected private
Модификатор доступа ↓
public public protected private
protected protected protected private
private нет доступа нет доступа нет доступа

Конструкторы и деструкторы при наследовании Как базовый, так и производный классы могут иметь конструкторы и деструкторы. Если и у базового и у производного классов есть конструкторы и деструкторы, то конструкторы выполняются в порядке наследования, а деструкторы – в обратном порядке. То есть если А – базовый класс, В – производный из А, а С – производный из В (А-В-С), то при создании объекта класса С вызов конструкторов будет иметь следующий порядок:

  1. конструктор класса А
  2. конструктор класса В
  3. конструктор класса С.

Вызов деструкторов при удалении этого объекта произойдет в обратном порядке:

  1. деструктор класса С
  2. деструктор класса В
  3. деструктор класса А.

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

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

КонструкторПроизводногоКласса (СписокФормальныхАргументов)
: КонструкторБазовогоКласса (СписокФактическихАргументов)
{ 
    // тело конструктора производного класса 
}

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

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();
};

results matching ""

    No results matching ""