КАТЕГОРИИ: Архитектура-(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; Просмотров: 795; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |