Множественное наследование
Одной из отличительных особенностей языка C++ является множественное наследование, при таком наследовании класс наследник может получить поля и методы от 2-х и более классов родителей.
Рассмотрим на примере:
Пусть у нас будут базовые классы студент и работник и класс наследник работающий студент.
class Student
{
public:
Student ()
{
this->Name="Ivan";
this->course=1;
}
void getInfoSt(){cout<Name<< " "<course <<" course"<<endl;}
string Name;
unsigned int course; //указатель на переменную в динамической памяти
};
class Employer
{
public:
Employer ()
{
this->position="manager";
}
void getInfoEmp(){cout<position<<endl;}
string position;
};
class WorkingStudent : public Student, public Employer
{
public:
void getInfoWS()
{
cout<Name<<", "<course <<" course"<<" also working as "<position<<endl;
}
};
В main напишем код:
WorkingStudent c_WS;
c_WS.getInfoWS();
В результате в консоль выведется «Ivan, 1 course also working as manager»
Также мы можем вызвать методы классов-родителей, например:
//Методы классов-родителей
c_WS.getInfoSt();
c_WS.getInfoEmp();
Как это произошло:
Т.к. класс работающего студента мы наследуем от 2-х классов студент и работник, то вызываются конструкторы (в данном случае по умолчанию) для этих классов:
- сначала для работающего студента
- потом для студента, т.к. он стоит первым в записи class WorkingStudent : public Student…
- последним для работника
В результате мы проинициализировали поля, а класс работающего студента получил все поля и методы классов-родителей (стоит отметить, что пока что эти поля и методы имеют разные имена).
Далее мы можем использовать эти поля и методы в классе наследнике, а также вызывать методы базовых классов у объектов класса WorkingStudent.
При уничтожении (выходе из зоны видимости) класса WorkingStudent вызываются деструкторы в порядке, обратном конструкторам:
- сначала для работника
- потом для студента
- самым последним вызывается деструктор класса-наследника WorkingStudent
Почему такой порядок – как говорилось ранее – функции и классы вызываются с инкрементом адресов и удаляются с декрементом – следовательно «вверху» памяти будет базовый класс, потом первый предок и потом следующие предки.
Т.е.
child();
parent1();
parent2();
//code
~parent2();
~parent1();
~child();
Вызываемые функции ставятся «ниже» кода класса, и по мере выполнения удаляются из стека, пока не дойдут до деструктора, после удаления деструктора открывается следующий класс и тд.
Примечание: фразы «выше» и «ниже» относительно памяти в данном контексте условные, обычно старту программы соответствуют «меньшие по номиналу адреса» , а середине программы «большие» просто понятия выше и ниже больше перекликаются с кодом программы и ранее нарисованными таблицами адресов.
Множественное наследование, вызов одинаковых методов:
В предыдущем примере мы использовали различные имена методов, но что делать, если мы хотим использовать одинаковое имя?
изменим имена методов getInfoSt, getInfoEmp на getInfo
А в main напишем код:
//Методы классов-родителей
((Student)c_WS).getInfo();
((Employer)c_WS).getInfo();
В этом случае мы делаем приведение к типу класс «студент» или «работник» и у них вызываем требуемый метод.
Вызов конструкторов базовых классов, отличных от конструкторов по умолчанию:
Если требуется вызвать конструктор базовых классов, отличный от конструкторов по умолчанию, то можно сделать конструктор класса-наследника Child так:
Child(arg1, arg2) : Parent1(arg1), Parent2(arg2)
{
}