Рассмотрим две проблемы, которые возникают при множественном наследовании: конфликт имен между суперклассами и повторное наследование.
Конфликт имен происходит тогда, когда в двух или более суперклассах случайно оказывается элемент (переменная или операция) с одинаковым именем.
Пример. Определим абстракцию «Работающий студент». Для этого введем более общие абстракции «Работник» и «Студент». Абстракция «Работающий студент» будет наследовать компоненты обеих общих абстракций.
class Worker {
public:
int ID_profession; // код профессии
char* Name; // имя
};
class Student {
public:
int ID_university; // код университета
char* Name; // имя
};
class Student_Worker: public Student, public Worker {... };
Рассмотрим последовательность действий
Student_Worker He;
...
He.ID_profession; // правильно
He.Name; // неправильно – двусмысленно
Конфликт имен элементов подкласса может быть разрешен полной квалификацией имени члена класса, т.е. к именам добавляют префиксы, которые указывают имена тех классов, откуда они пришли.
He.Worker:: Name; // правильно
Повторное наследование возникает тогда, когда в многоуровневой наследственной иерархии какой-либо класс дважды является суперклассом для другого класса.
|
|
Продолжим пример с работающим студентом. Анализируя глубже полученную иерархию наследования, мы обнаружим, что и работник, и студент имеют ряд общих признаков, в частности, имя. Разумно ввести еще более общую абстракцию «Человек».
class Person {
public: char* Name; // имя
}
class Worker: public Person {
public: int ID_profession; // код профессии
}
class Student: public Person {
public: int ID_university; // код университета
}
Наследственная иерархия класса Student_Worker представлена на рис. 4.1.
Рис. 4.1 Наследственная иерархия Рис. 4.2 Ромбовидная структура
класса Student_Worker наследования
Для доступа к одной из копий унаследованного элемента необходимо воспользоваться явной квалификацией, т.е. добавить к его имени префикс в виде имени класса-источника.
He.ID_profession; // правильно
He.Name; // неправильно – двусмысленно
He.Person:: Name; // неправильно – двусмысленно
He.Worker:: Name; // правильно
He.Student:: Name; // правильно
Продолжая анализ полученной иерархии, заметим, что работающий студент имеет всего одно имя. В результате объект класса Student_Worker должен использовать единственную копию элемента Name, унаследованную от Person. В результате приходим к ромбовидной структуре наследования, когда повторяющийся суперкласс в производном классе представлен одним и тем же (совместно используемым) объектом (см. рис 4.2).
В С++ механизмом задания ромбовидной структуры наследования является виртуальное наследование, когда повторяющийся суперкласс объявляется «виртуальным базовым классом». Для задания виртуального наследования используется синтаксис следующего примера.
|
|
class Person {...};
class Worker: public virtual Person {...};
class Student: public virtual Person {...};
class Student_Worker: public Student, public Worker {... };
Задача. Укажите ошибочные строки в функции main среди отмеченных буквами А, Б, В, Г, Д, Е.
class Transport // Транспортное средство
{public: String Registration_Number; // регистрационный номер
};
class Land_Transport: public Transport { // Сухопутное транспортное средство
public: int Shaft; // ведущая ось
};
class Water_Transport: public Transport { // Водное транспортное средство
public: int Displacement; // водоизмещение
};
class Amphibia: public Land_Transport, public Water_Transport {}; // Амфибия
void main() {
Amphibia amph;
...
amph.Shaft; // А
amph.Displacement; // Б
amph.Water_Transport:: Displacement; // В
amph.Registration_Number; // Г
amph.Water_Transport:: Registration_Number; // Д
amph.Transport:: Registration_Number; // Е
}