Лекции.Орг


Поиск:




Категории:

Астрономия
Биология
География
Другие языки
Интернет
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Механика
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Транспорт
Физика
Философия
Финансы
Химия
Экология
Экономика
Электроника

 

 

 

 


Множественное наследование




 

Рассмотрим две проблемы, которые возникают при множественном наследовании: конфликт имен между суперклассами и повторное наследование.

Конфликт имен происходит тогда, когда в двух или более суперклассах случайно оказывается элемент (переменная или операция) с одинаковым именем.

Пример. Определим абстракцию "Работающий студент". Для этого введем более общие абстракции "Работник" и "Студент". Абстракция "Работающий студент" будет наследовать компоненты обеих общих абстракций.

 

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 Наследственная иерархия класса Student_Worker

 

Для доступа к одной из копий унаследованного эле­мента необходимо воспользоваться явной квалификацией, т.е. добавить к его имени префикс в виде имени класса-источника.

 

He.ID_profession; // правильно

He.Name; // неправильно – двусмысленно

He.Person:: Name; // неправильно – двусмысленно

He.Worker:: Name; // правильно

He.Student:: Name; // правильно

 

Продолжая анализ полученной иерархии, заметим, что работающий студент имеет всего одно имя. В результате объект класса Student_Worker должен использовать единственную копию эле­мента Name, унаследованную от Person. В результате приходим к ромбовидной структуре наследования для класса Student_Worker, представленной на рис 4.2.

 

 

Рис. 4.2 Ромбовидная структура наследования для класса Student_Worker

 

Одним из механизмов задания такого совместного использования является виртуальный базовый класс. Виртуальный базовый класс в производном классе представлен одним и тем же (совместно используемым) объектом. Для задания виртуального наследования используется синтаксис следующего примера.

 

class Person {...};

class Worker: public virtual Person {...};

class Student: public virtual Person {...};

class Student_Worker: public Student, public Worker {... };

 

Зависимость

 

Пример. Пусть управление температурой каждый объект класса Controller осуществляет в соответствии с задаваемым ему планом. План представим в виде экземпляра класса Plan.

 

class Plan;

class Controller{

...

void process (Plan&);

...

};

 

Класс Plan упомянут как часть описания функции-члена process; это дает нам основание сказать, что класс Controller пользуется ус­лугами класса Plan.

Отношение зависимости (использования) между классами означает, что изменение в спецификации одного класса может повлиять на другой класс, который его использует, причем обратное в общем случае неверно. Можно сказать, что один из классов (клиент) пользуется услугами дру­гого (сервера).

Один класс может использовать другой по-разному. В нашем примере это происходит при описании интерфейсной функции. Отношение использования также имеет место, если в реализации какой-либо операции происходит объявление локаль­ного объекта используемого класса.

 

Инстанцирование

 

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

 

class IntStack {

int stack[100];

...

};

class ControllerStack {

Controller* stack[100];

...

};

 

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

 

class Stack {

void* stack[100];

...
};

 

Однако это не безопасно с точки зрения типов. Никто не гарантирует нам, что пользователь не поместит в стек элемент одного типа, а взять захочет элемент другого типа.

Для реализации нашей идеи необходимо воспользоваться шаблоном или параметризованным классом. Шаблон служит для построения других классов и может быть пара­метризован другими классами, объектами или операциями.Использование шаблонов реализует в языке С++ особый тип полиморфизма – параметрический полиморфизм.

 

template <class Тype> class Stack {

Тype stack[100];

...

public:

void push (Тype);

Т рор ();

...

};

 

Префикс template < class Тype > делает Тype параметром объявления, которому этот пре­фикс предшествует.

Инстанцирование – подстановка фактических параметров шаблона вместо формальных. В результате создается конкретный класс, который может иметь экземпляры.

Объявим нужные нам стеки:

 

typedef Stack < int > IntStack // синоним класса стеков целых чисел

typedef Stack < Controller* > ControllerStack // синоним класса стеков

// контроллеров

IntStack IS; // стек для целых чисел

ControllerStack CS; // стек для контроллеров

 

Объекты IS и CS – это экземпляры совершенно различных классов, которые даже не имеют общего суперкласса. Тем не менее они получены из одного параметризованного класса Stack.

Инстанцирование безопасно с точки зрения типов. По правилам C++ бу­дет отвергнута любая попытка поместить в стек или извлечь из него что-либо, кроме целых чисел или указателей на экземпляры класса Controller, соответственно.

В языке С++ можно определять шаблоны не только классов, но и функций. В качестве примера рассмотрим определение шаблона функции, служащей для определения максимального из двух элементов.

 

template <class Тype > Тype max(Тype x, Тype y){

return (x > y)? x: y;

};

 

Теперь мы можем использовать один и тот же шаблон для целых и вещественных чисел.

 

int i, j, k;

double a, b, c;

...

k = max <int> (i, j);

c = max <double> (a, b);

 

Кроме того, возможно использовать этот шаблон и для объектов некоторого класса, если в нем определена операция ">".

 





Поделиться с друзьями:


Дата добавления: 2016-07-29; Мы поможем в написании ваших работ!; просмотров: 493 | Нарушение авторских прав


Поиск на сайте:

Лучшие изречения:

Настоящая ответственность бывает только личной. © Фазиль Искандер
==> читать все изречения...

2364 - | 2087 -


© 2015-2025 lektsii.org - Контакты - Последнее добавление

Ген: 0.01 с.