39
ЛАБОРАТОРНАЯ РАБОТА № 9.
ПРОИЗВОДНЫЕ КЛАССЫ, НАСЛЕДОВАНИЕ
Ключевые слова: наследование, базовый класс, виртуальный ме-
тод, полиморфизм, производный класс, абстрактный класс, чисто вирту-
альный метод, множественное наследование, виртуальное наследование
виртуальный деструктор.
Цель: Познакомится с наследованием, видами наследования, вир-
туальными методами, абстрактными классами и множественным насле-
дованием.
Наследование – это такое отношение между классами, когда один
класс частично или полностью повторяет структуру и поведение другого
класса (одиночное наследование) или других (множественное наследова-
ние) классов. Наследование устанавливает
между классами иерархию
«общее-частное».
Связи наследования также называют обобщениями (
generalization)
и изображают в виде стрелок от класса-потомка к классу-предку.
Рассмотрим пример – учет сотрудников организации. Каждый со-
трудник – это, прежде всего, человек. Студент, работник, руководитель –
частные случаи объекта «человек», определяющего общие характери-
стики для всех своих разновидностей. Организация этих понятий в иерар-
хию позволяет избежать повторения кода и обращаться с объектами про-
изводных классов как с объектами базового.
Возьмем за основу иерархии класс
Person,
немного изменив его
структуру ради соответствия концепции наследования. Поскольку для
всех объектов кадровой структуры требуется знать имя, фамилию,
а также год рождения для определения возраста, объявим данные атри-
буты как
protected, заменив заодно тип представления имени и отчества
на более удобный в работе тип
String.
class Person
{
public:
void set_year(int value);
void set_sirname(string & value);
void set_name(string & value);
const string & get_name() const;
const int get_year() const;
const string & get_sirname() const;
40
protected:
String name;
String sirname;
int year;
};
Работник (
Employee) отличается от просто человека (
Person) тем,
что работает в некотором подразделении (
Department) и получает опре-
деленную заработную плату. Руководитель (
Manager), являясь, в
свою
очередь работником предприятия, отвечает за определенную группу под-
чиненных и может получать заработную плату, складывающуюся из
основной ставки и надбавки в виде процента от заработной платы своих
работников.
Выделять новый класс из существующего стоит лишь тогда, когда
он знает или делает то, чего не знает и не делает объект базового класса.
Это означает присутствие в производном
классе атрибута или метода,
неприменимого для объектов класса-предка.
Механизм наследования классов позволяет строить иерархии, в ко-
торых производные классы получают элементы родительских, или базо-
вых, классов и могут дополнять их или изменять их свойства. Классы,
находящиеся ближе к началу иерархии, объединяют в себе наиболее
общие черты для всех нижележащих классов. По мере продвижения вниз
по иерархии классы все больше приобретают конкретные особенности:
Конструкторы не наследуются, поэтому производный класс должен
иметь собственные конструкторы.
Порядок вызова конструкторов:
Если в конструкторе производного класса явный вызов конструктора
базового класса отсутствует, автоматически вызывается конструктор
базового класса по умолчанию (без параметров).
Для иерархии, состоящей из нескольких уровней, конструкторы базо-
вых классов вызываются, начиная с самого верхнего уровня. После
этого выполняются конструкторы тех элементов класса,
которые
являются объектами, в порядке их объявления в классе, а затем испол-
няется конструктор класса.
В случае нескольких базовых классов их конструкторы вызываются
в порядке объявления.
Операция присваивания не наследуется и, поэтому ее также требу-
ется явно определить.
41
Деструкторы не наследуются, и если программист не описал в про-
изводном классе деструктор, он формируется по умолчанию и вызывает
деструкторы всех базовых классов.
В отличие от конструкторов, при написании деструктора произ-
водного класса в нем не требуется явно вызывать деструкторы базовых
классов, это будет сделано автоматически.
Для иерархии классов, состоящей из нескольких уровней, деструк-
торы вызываются в порядке, строго обратном вызову конструкторов: сна-
чала
вызывается деструктор класса, затем – деструкторы элементов
класса, а потом деструктор базового класса.
Доступ к объектам иерархии классов лучше всего осуществляется
через указатели на базовый тип. При наследовании со спецификатором
public такому указателю можно присваивать адрес любого производного
класса. Проблема заключается в том,
что компилятор не будет знать,
с каким именно классом связан указатель. Например, мы имеем в наличии
трех работников: двух подчиненных (
A и
B) и их руководителя
C, состав-
ляющих множество
E. Им назначена зарплата в 3300, 4400 и 5500 рублей
соответственно. У нас стоит задача представить фактические доходы
работников, которые будут определяться не только окладом.
void main()
{
Employee A, B;
Manager C;
Employee* p[3];
p[0] = & A; p[1] = & B; p[2] = & C;
C.Employers.push_back(& A);
C.Employers.push_back(& B);
p[0]->set_salary(3300);
p[1]->set_salary(4400);
p[2]->set_salary(5500);
for (i = 0; i < 3; i++)
{
cout << p[i]->get_salary() << endl;
}
}
42
Отметим, что во время компиляции не будет известно, на
какой
объект указывает
p[i], следовательно, класс будет определяться по типу
указателя. Соответственно будет выбран и правильный метод для вычис-
ления заработной платы.
В C++ реализован механизм позднего связывания, когда разреше-
ние ссылок на метод происходит на этапе выполнения программы в зави-
симости от конкретного типа объекта, вызвавшего метод. Этот механизм
реализован с помощью виртуальных методов. Для определения виртуаль-
ного метода используется спецификатор
virtual. Правила описания и ис-
пользования виртуальных методов:
Если в базовом классе метод определен как виртуальный, метод, опре-
деленный в производном классе с тем же именем и набором парамет-
ров, автоматически становится виртуальным, а с отличающимся набо-
ром параметров – обычным.
Виртуальные методы наследуются, т. е. переопределять их в производ-
ном классе требуется только при необходимости задать отличающиеся
действия. Права доступа при переопределении изменить нельзя.
Если виртуальный метод переопределен в производном классе, объ-
екты этого класса могут получить доступ к
методу базового класса
с помощью операции доступа к области видимости.
Виртуальный метод не может объявляться с модификатором
static,
но может быть объявлен как дружественный.
Достарыңызбен бөлісу: