На этом шаге мы приведем общие сведения о множественном наследовании.
Начнем с определений.
Класс называют непосредственным (прямым) базовым классом (прямой базой), если он входит в список базовых при определении класса. В то же время для производного класса могут существовать косвенные или непрямые предшественники, которые служат базовыми для классов, входящих в список базовых. Если некоторый класс A является базовым для B и B есть база для C, то класс B является непосредственным базовым классом для C, а класс А - непрямой базовый класс для C (рисунок 1).
Рис.1. Прямое и косвенное наследование классов
Обращение к компоненту XA, входящему в A и унаследованному последовательно классами B и C, можно обозначить в классе C либо как А::XA, либо как В::XA. Обе конструкции обеспечивают обращение к элементу XA класса А.
Иерархию производных классов удобно представлять с помощью направленного ациклического графа (НАГ), где стрелкой изображают отношение "производный от". Производные классы принято изображать ниже базовых. Именно в таком порядке их объявления рассматривает компилятор и их тексты размещаются в листинге программы. Класс может иметь несколько непосредственных базовых классов, т.е. может быть порожден из любого числа базовых классов, например:
class X1 { ... }; class X2 { ... } ; class ХЗ { ... } ; class Y1: public X1, public X2, public X3 { ... };
Наличие нескольких прямых базовых классов называют множественным наследованием. В качестве примера рассмотрим производный класс "звезда, вписанная в квадрат". Базовыми классами будут: звезда (star) и квадрат (square). Класс "звезда" был описан раннее (он базируется на класе point), а класс square определим следующим образом:
// SQUARE.CPP - класс "квадрат". #ifndef SQUARE #define SQUARE 1 #include <conio.h> //Для функции gotoxy(); class square { int xq,yq, //Координаты центра квадрата. lq; //Длина стороны квадрата. //Вспомогательная функция рисования: void rissquare(void); public: // Конструктор класса SQUARE: square (int xi, int yi, int li) { xq = xi; yq = yi; lq = li; } ~square() // Деструктор класса SQUARE. { hide(); // Убрать с экрана изображение квадрата. } void hide(); // Убрать с экрана изображение квадрата. void show() //Изобразить квадрат на экране дисплея. { rissquare(); } }; //Конец определения класса. void square::rissquare(void) { int d=lq/2; for (int i=-d; i<=d; i++) { gotoxy(xq+i,yq+d); cout << "="; gotoxy(xq+i,yq-d); cout << "="; } for (int j=-d; j<=d; j++) { gotoxy(xq-d,yq+j); cout << "="; gotoxy(xq+d,yq+j); cout << "="; } } void square::hide() // Убрать с экрана изображение квадрата. { // Стереть изображение с экрана: int d=lq/2; for (int i=-d; i<=d; i++) { gotoxy(xq+i,yq+d); cout << " "; gotoxy(xq+i,yq-d); cout << " "; } for (int j=-d; j<=d; j++) { gotoxy(xq-d,yq+j); cout << " "; gotoxy(xq+d,yq+j); cout << " "; } } #endif
В следующей программе на основе классов star и square создан производный класс "звезда в квадрате" с именем starsqrt:
#include "square.cpp" #include "star.cpp" class starsqrt: public star, public square { public: starsqrt (int xi, int yi, int ri): star(xi,yi,ri), square (xi,yi,2*ri) {} void show() { star::show(); square::show(); } void hide () { star::hide(); square::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(); }
Определения базовых классов должны предшествовать их использованию в качестве базовых. Поэтому тексты из файлов square.срр и star.cpp включены в начало программы, после чего описывается класс starsqrt. В производном классе starsqrt телом конструктора служит пустой оператор. Выполнение конструктора starsqrt() сводится к последовательному вызову конструкторов базовых классов. При этом за счет соответствующего выбора параметров центры обеих фигур (квадрата и звезды) совпадают. Кроме того, длина стороны квадрата выбирается равной удвоенной длине луча звезды (параметр 2*ri), и тем самым звезда оказывается вписанной в квадрат.
В основной программе формируются два объекта A, B класса starsqrt. Они последовательно выводятся на экран дисплея и в обратном порядке убираются с экрана.
На следующем шаге мы продолжим знакомство с множественным наследованием.