На этом шаге мы рассмотрим проблему дублирования базовых классов.
При множественном наследовании никакой класс не может больше одного раза использоваться в качестве непосредственного базового. Однако класс может больше одного раза быть непрямым базовым классом:
class X { ...; f(); ... }; class Y: public X { ... }; class Z: public X { ... }; class D: public Y, public Z { ... };
В данном примере класс X дважды опосредованно наследуется классом D. Особенно хорошо это видно в направленном ациклическом графе (НАГ):
Рис.1. Двойное наследование класса X
Проиллюстрированное дублирование класса соответствует включению в производный объект нескольких объектов базового класса. В нашем примере существуют два объекта класса X, и поэтому для устранения возможных неоднозначностей вне объектов класса D нужно обращаться к конкретному компоненту класса X, используя полную квалификацию: D::Y::X::f() или D::Z::Х::f(). Внутри объекта класса D обращения упрощаются: Y::Х::f() или Z::Х::f(), но тоже содержат квалификацию.
В качестве содержательного примера с дублированием непрямого базового класса изменим класс square) так, чтобы он был наследникм класса point). Новый класс назовем square1:
// SQUARE1.CPP - класс "квадрат", наследник класса POINT. #ifndef SQUARE1 #define SQUARE1 1 #include <conio.h> //Для функции gotoxy(); #include "point.cpp" //Определение класса POINT. class square1 : public point { int lq; //Длина стороны квадрата. //Вспомогательная функция рисования: void rissquare(void); public: // Конструктор класса SQUARE1: square1 (int xi, int yi, int li): point(xi,yi) { lq = li;} ~square1() // Деструктор класса SQUARE1. { hide(); // Убрать с экрана изображение квадрата. } void hide(); // Убрать с экрана изображение квадрата. void show() //Изобразить квадрат на экране дисплея. { rissquare(); } }; //Конец определения класса. void square1::rissquare(void) { int d=lq/2; for (int i=-d; i<=d; i++) { gotoxy(x+i,y+d); cout << "="; gotoxy(x+i,y-d); cout << "="; } for (int j=-d; j<=d; j++) { gotoxy(x-d,y+j); cout << "="; gotoxy(x+d,y+j); cout << "="; } } void square1::hide() // Убрать с экрана изображение квадрата. { // Стереть изображение с экрана: int d=lq/2; for (int i=-d; i<=d; i++) { gotoxy(x+i,y+d); cout << " "; gotoxy(x+i,y-d); cout << " "; } for (int j=-d; j<=d; j++) { gotoxy(x-d,y+j); cout << " "; gotoxy(x+d,y+j); cout << " "; } } #endif
Текст программы, использующей классы square1 и (star), остался без изменений:
#include<iostream.h> #include "square1.cpp" #include "star.cpp" class starsqrt: public star, public square1 { public: starsqrt (int xi, int yi, int ri): star(xi,yi,ri), square1 (xi,yi,2*ri) {} void show() { star::show(); square1::show(); } void hide () { star::hide(); square1::hide(); } }; void main() { starsqrt A(5,7,4); starsqrt B(18,12,6); A.show(); getch(); B.show(); getch(); A.hide(); getch(); B.hide(); getch(); }
Как избежать двойного наследования базовых классов? Ответ на этот вопрос будет дан на следующем шаге.
На следующем шаге мы рассмотрим виртуальные базовые классы.