Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Void show(void)




Void riesquare(void)

Class square

(int xq, yq, // Координаты центра квадрата

lq; // Длина стороны квадрата

// Вспомогательная функция рисования:

{

int d = lq/2;

line(xq-d,yq-d,xq+d,yq-d);

line(xq-d,yq+d,xq+d,yq+d);

line(xq-d,yq-d,xq-d,yq+d);

line(xq+d,yq-d,xq+d,yq+d);)

public:

// Конструктор:

square(int xi,int yi,int li)

{ xq = xi; yq = yi; lq = li; }

// Изобразить квадрат на экране:

void show()

{ rissquare(); }

// Убрать с экрана изображение квадрата:

void hide()

{ int bk, cc;

bk = getbkcolor(); // Цвет фона

cc = getcolor(); // Цвет изображения

setcolor(bk); // Сменить цвет рисования

rissquare(); // Рисуем квадрат цветом фона

setcolor(cc); // Восстановить цвет изображения

}

};

В следующей программе на основе классов circ и square создан производный класс "окружность в квадрате" с именем circsqrt:

//Р10-02.СРР - окружность в квадрате - множественное

// наследование

#include <conio.h> // Для функции getch()

#include "square.cpp" // Определение класса "квадрат"

#include "circ.cpp" // Определение класса "окружность"

// Производный класс - "окружность, вписанная в квадрат";

// Класс circsqrt наследует только методы обоих базовых

// классов. В нем нет наследуемых данных.

class circsqrt: public circ, public square

{ public:

// Конструктор:

circsqrt(int xi, int yi, int ri):

circ(xi,yi,ri), // Явно вызываются конструкторы

square(xi,yi,2*ri) // базовых классов

{}

// Изобразить на экране окружность в квадрате:

{ circ::show(); square::show(); }

// Убрать с экрана изображение:

void hide()

{ square::hide(); circ::hide (); }

};

void main()

{ int dr = DETECT, nod;

initgraph(&dr,&mod,"с:\\borlandc\\bgi");

circsqrt Al(100,100,60);

circsqrt F4(400,300,50); A1.3how(); getch(); F4.3how(); getch(); F4.hide(); getch(); Al.hide(); getch(); closegraph(); }

Определения базовых классов должны предшествовать их исполь­зованию в качестве базовых. Поэтому тексты из файлов square. cpp и circ.cpp включены в начало программы, после чего описывается класс circaqrt. В производном классе circsqrt телом конструктора служит пустой оператор. Выполнение конструктора circsqrt () сво­дится к последовательному вызову конструкторов базовых классов. При этом за счет соответствующего выбора параметров центры обеих фигур (квадрата и окружности) совпадают. Кроме того, длина сторо­ны квадрата выбирается равной удвоенному радиусу окружности (параметр 2 * ri), и тем самым окружность оказывается вписанной в квадрат.

В основной программе формируются два объекта a1, F4 класса circsqrt. Они последовательно выводятся на экран дисплея (рис. 10.4) и в обратном порядке убираются с экрана.

Рис. 10.4. Последовательность изображений на экране при выполнении программы Р10-02. СРР

При множественном наследовании никакой класс не может больше одного раза использоваться в качестве непосредственного базового. Однако класс может больше одного раза быть непрямым базовым классом:

class X {...; f ();... }; class У: public X {...); class Z: public X {... }; class D: public Y, public Z {...);

В данном примере класс х дважды опосредованно наследуется классом D. Особенно хорошо это видно в направленном ациклическом графе (НАГ):

Проиллюстрированное дублирование класса соответствует вклю­чению в производный объект нескольких объектов базового класса. В нашем примере существуют два объекта класса х, и поэтому для устранения возможных неоднозначностей вне объектов класса D нуж­но обращаться к конкретному компоненту класса х, используя полную квалификацию: D::Y::X::f() ИЛИ D::Z::X::f(). Внутри объекта класса d обращения упрощаются: У::х::f о или Z::X::f(), но тоже содержат квалификацию.

В качестве содержательного примера с дублированием непрямого базового класса рассмотрим программу, в которой определен класс spotelli - круглое пятно, вписанное в эллипс. Класс spotelli непо­средственно базируется на классах spot и ellips, каждый из которых базируется, в свою очередь, на классе point. Таким образом, point дважды входит в spotalli в качестве непрямого базового класса, т.е. дублируется. Класс point (точка на экране) уже рассматривался. Текст его определения находится в файле point.срр (см. п. 9.4). Опре­деление производного от класса point класса spot (круглое пятно на экране) находится в файле spot.cpp (см. п. 10.1). На базе класса point можно следующим образом определить класс "эллипс":

//ELLIPS.CPP - класс "эллипс"

#ifnde£ ELLIPS

#define ELLIPS 1

#include "point.cpp" // Определение класса point

class ellips: public point

{ protected: int rx,ry; // Радиусы эллипса public:

// Конструктор:

ellips (int xc, int yc, int rx, int ry):

point (xc,yc)

{ this->rx = rx; this->ry = ry; }

void show() // Изобразить на экране эллипс

{ ellipse(x,y,0,360,rx,ry);

return; } // Убрать с экрана изображение эллипса:

void hide() { int cc, bk;

cc = getcolor ();

bk = getbkcolor ();

setcolor (bk);

ellipse(x,y,O,360,rx,ry);

setcolor(cc); }

};

# endif

Как уже отмечалось, определение базового класса должно предшествовать его упоминанию в списке базовых классов. Поэтому в начале текстов spot.cpp и ellips.cpp помещена препроцессорная директива включения текста определения класса point.

В классе ellips конструктор предусматривает задание четырех параметров: координаты центра (хс, ус) и радиусы вдоль осей (rх, гу). Координаты хс, ус используются в качестве параметров при вызове конструктора базового класса point. Чтобы различить компоненты rх, rу класса ellips и обозначенные теми же идентификатора-ми формальные параметры конструктора, используется указатель this. В классе ellips две общедоступные функции show() - изобразить эллипс на экране дисплея; hide() - убрать с экрана изображение эллипса.

Текст программы:

//Р10-03.СРР - круглое пятно в эллипсе - множественное

// наследование с дублированием базовых

// классов (дублируется класс point)

#include "spot.cpp"

#include "eilips.cpp"

// Производный класс - дважды косвенно наследующий

// класс point:

class spotelli: public spot, public ellips { // Вспомогательная функция:

int min(int valuel, int value2)

{ return ((valuel < value2) (valuel: value2); }

public:

// Конструктор:

spotelli(int xi,int yi,int rx,int ry): ellips(xi,yi,rx,ry), spot(xi,yi,min(rx,ry))

{}

// Вывести изображение на экран дисплея:

void show()

(spot::show();

ellips::show(); }

void hide() // Убрать изображение с экрана дисплея

{ spot::hide();

ellips::hide(); }

};

#include <conio.h> // Для функции

getch()

void main() { int dr = DETECT, mod;

initgraph(&dr,&mod,"c:\\borlandc\\bgi"); { spotelli Al (100,100,20,80); spotelli F4(400,300,230,100); Al.ahow(); getch(); F4.show(); getch(); F4.hide(); getch(); Al.hide(); }

closegraph(); }

В классе ellips, в классе spot и в классе spotelli наследуются данные х, у класса point - координаты точки на экране. В классе point они определены как защищенные (protected) и сохраняют тот же статус доступа в производных классах, где определяют координаты центров- пятна (класс spot), эллипса (класс ellips) и эллипса с пят­ном (класс spotelli).

Класс spot мы уже разбирали.

Рис. 10.5. Последовательность изображений на экране при выполнении программы Р10-03.СРР

Конструктор класса spotelli не выполняет никаких дополни­тельных действий - последовательно вызываются конструкторы клас­са ellips и класса spot, причем центры создаваемых фигур совпадают, а в качестве радиуса пятна выбирается меньший из радиу­сов эллипса. Используемая в этом случае функция min() определена по умолчанию как встроенная (inline) собственная (private) функция класса spotelli.

Чтобы отличать одинаково обозначенные функции, унаследован­ные классом spotelli из классов spot и eliips, при вызове show о и hide () используются полные квалифицированные имена, в которых применена операция '::'.

Функция main () не содержит ничего нового. Описаны два объекта Al, F4 класса spotelli, которые последовательно изображаются на экране и "стираются" с него.

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

class X {... f ();... }; class Y: virtual public X {... }; class Z: virtual public X {... }; class D: public Y, public Z {... };

Теперь класс d будет включать только один экземпляр х, доступ к которому равноправно имеют классы Y и z. Графически это очень на­глядно:

Иллюстрацией сказанного может служить иерархия классов в сле­дующей программе:

//Р10-04.СРР - множественное наследование с виртуальным

// базовым классом

#include <iostream.h>

class base // Первичный (основной) базовый класс

{ int jj; char cc; char w[10]; public:

base(int j = 0, char с = '*')

{ jj = j;

cc = c; }

};

class dbase: public virtual base { double dd; public:

dbase(double d = 0.0): base()

{ dd = d; } };

class fbase: public virtual base { float ff; public:

fbase(float f = 0.0): base() { ff = f; } };

class top: public dbase, public fbase { long tt; public:

top (long t = 0): dbase (), fbase () { tt - t; } };

void main()

{

cout << "ЧпОсновной базовый класс: sizeof(base) = " << sizeof(base);

cout << "^Непосредственная база: sizeof(dbase) = " << sizeof(dbase);

cout << "^Непосредственная база: sizeof(fbase) = " << sizeof(fbase);

cout << "ЧпПроизводный класс: sizeof(top) = " << sizeof(top); }

Результаты выполнения программы:

Основной базовый класс: sizeof(base) = 13

Непосредственная база: sizeof (dbase) = 23

Непосредственная база: sizeof(fbase) =19 Производный класс: sizeof(top) = 33

Основной базовый класс base в соответствии с размерами своих компонентов стандартных типов int и char [11] имеет размер 13 байт. Создаваемые на его основе классы dbase и fbase занимают соответ­ственно 23 и 19 байт. (В dbase входит переменная типа double, зани­мающая 8 байт, наследуется базовый класс base, для которого требуется 13 байт, и 2 байта нужны для связи в иерархии виртуальных классов.) Производный класс top включает: один экземпляр базового класса base (13 байт); данные и связи класса dbase (10 байт); данные и связи класса fbase (6 байт); компонент long tt (4 байта).

Если в той же программе убрать требование виртуальности (атрибут virtual) при наследовании base в классах dbase и fbase, то результаты будут такими:

Основной базовый класс: sizeof(base) = 13

Непосредственная база: sizeof(dbase) = 21

Непосредственная база: sizeof(fbase) = 17

Производный класс: sizeof(top) = 42

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

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

class X {... };

class Y: virtual public X {... }; class Z: virtual public X {... }; class B: virtual public X {.. }; class C: virtual public X {... }; class E: public X {...); class D: public X {... }; class A: public D, public B, public Y, public Z, public C, public E {... };

В данном примере объект класса а включает три экземпляра объ­ектов класса х: один виртуальный, совместно используемый классами в, Y, с, z, и два невиртуальных относящихся соответственно к классам D и е. Таким образом, можно констатировать, что виртуальность класса в иерархии производных классов является не свойством класса как такового, а результатом особенностей процедуры наследования.

Возможны и другие комбинации виртуальных и невиртуальных базовых классов. Например:

class ВВ {... };

class АА: virtual public ВВ {... };

class CC: virtual public ВВ {... };

class DD: public AA, public CC, public virtual BB {... };

Соответствующий НАГ имеет вид:

При использовании наследования и множественного наследования могут возникать неоднозначности при доступе к одноименным ком­понентам разных базовых классов. Простейший и самый надежный способ устранения неоднозначностей - использование квалифициро­ванных имен компонентов. Как обычно, для квалификации имени компонента используется имя класса. Следующий пример иллюстри­рует упомянутую неоднозначность и ее разрешение с помощью ква­лифицированных имен компонентов:

class X { public: int d;... }; class Y { public: int d;... }; class Z: public X, public Y, { public:

int d;

d - X::d + Y::d; };




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


Дата добавления: 2014-01-11; Просмотров: 740; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.009 сек.