Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
posobie1.doc
Скачиваний:
13
Добавлен:
01.05.2019
Размер:
457.22 Кб
Скачать

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

Как уже отмечалось, в С++ производный класс может быть порождён из любого числа непосредственных базовых классов. Наличие у производного класса более чем одного непосредственного базового класса называется множественным наследием. Синтаксически множественное наследование отличается от единичного наследования списком порождения, состоящим более чем из одного класса.

//Листинг26. Пример множественного наследования

class A

{int a1;

public:

int a2;

void funcA()

};

class B

{int b1;

public:

int b2;

void funcB()

};

class C: public A, public B //наследуем класс С от A и B

{int c1;

public:

int c2;

void funcC()

};

С хема иерархии классов, определенных в последнем примере, изображена на рис.7

Структура объекта класса будет аналогична изображенной на рис.4. Однако, если в списке базовых классов поменять местами объявление классов А и В, то есть определить класс С следующим образом:

class C: public B, public A

{ … };

то порядок следования компонент в объекте класса С изменится – в младших адресах будут располагаться компоненты объекта класса В, затем – объекта класса А.

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

class A {public: int x; void funcA(); …};

class B: public A {…};

class D: public A{…};

class C: public B, public D {…};

Д ублирование косвенного базового класса приводит к включению в производный класс нескольких объектов базового класса. Для класса С в последнем примере это означает, что компонентное данное x будет существовать в объектах данного класса в двух экземплярах – один унаследован через класс В, другой – через класс D. Структура объекта класса С изображена на рис. 9.

объект класса А, унаследованный через класс В

int x

объект класса В

объект класса А, унаследованный через класс D

int x

объект класса D

объект класса С

Рисунок 9. - Структура производного класса при множественном наследовании с дублированием косвенного базового класса.

При множественном наследовании зачастую возникает проблема неоднозначности при доступе к дублирующимся компонентам класса: неясно, какой из одноименных компонент изменится при следующем обращении

main()

{ C c;

c.x=6; // Ошибка!!!

}

Попытка доступа к члену данных x для объекта с приводит к ошибке транслятора “Member is ambiguous A::x and A::x”. Эта ошибка означает, что транслятор не может определить, какому из двух компонент x класса необходимо присвоить новое значение. Неразрешимыми именами для транслятора будут также следующие с.C::x и c.A::x. Решением проблемы является использование квалифицированных имен компонент с использованием имен классов B и D. Для транслятора однозначно различаются следующие имена компонент: с.B::x (компонента, унаследованная через класс В) и c.D::x (компонента, унаследованная через класс D).

Рассмотрим пример программы, реализующей множественное наследование. В программе реализованы класс Window, описывающий окно в текстовом режиме, и класс Text, описывающий буфер для хранения текстовой информации, а на их основе определен класс WinText, описывающий окно в текстовом режиме с возможностью отображения в нем текста.

//Листинг 27. Программа, использующая множественное наследование классов

#include<stdio.h>

#include<string.h>

#include <conio.h>

class Window //класс «окно в текстовом режиме»

{ protected:

int x,y; //координаты левого верхнего угла окна

int dx,dy; //размеры окна

int color,backcolor; //основной и фоновый цвета окна

public:

Window(int xb=10, int yb=10, int a=60,int b=20) //конструктор

{ x=xb;y=yb;dx=a;dy=b;

backcolor=1;color=15;}

void draw(); //функция изображения окна на экране

};

void Window::draw()

{ textbackground(backcolor);

for(int i=x;i<x+dx;i++)

for (int j=y;j<y+dy;j++)

{

gotoxy(i,j);

cprintf(" ");

}}

class Text //класс «текстовый буфер»

{ protected:

int n,UsedN; //n-количество строк в буфере, UsedN – количество реально используемых

//строк буфера

char **str; //указатель на начало буфера в памяти

public:

Text(int ); //конструктор

~Text(); //деструктор

void Read(char *filename); //функция чтения информации из файла в буфер

void Write(); //функция вывода информации из буфера на экран

};

Text::Text(int k) //конструктор динамически выделяет память под k строк…

{ n=k;UsedN=0;

str=new char*[n];

for(int i=0;i<n;i++)

str[i]=new char[80]; //…в каждой строке 80 символов

}

Text::~Text() //деструктор освобождает динамическую память из под буфера

{ for(int i=0;i<n;i++)

delete [] str[i];

delete [] str;

}

void Text::Read(char * filename)

{ UsedN=0;

FILE * fp;

if ((fp=fopen(filename,"r"))!=NULL) //открываем файл

{ while(!feof(fp)&&UsedN<n) //пока не конец файла или не заполнен весь буфер…

{ fgets(str[UsedN],80,fp); //считываем очередную строку из файла в буфер

UsedN++;

}

for(int i=0;i<UsedN;i++)

for (int j=0;j<80;j++)

if (str[i][j]=='\n')

{for(int k=j;k<80;k++)

str[i][k]=' '; //пробелами заполняем неиспользуемую часть буфера

break;}

fclose(fp);

}

else {strcpy(str[0],"Ошибка открытия файла"); //если открыть указанный файл

//не удалось записываем информацию об этом в буфер

for(int k=strlen(str[0]);k<80;k++)

str[0][k]=' ';

UsedN=1;

}}

void Text::Write() //функция постраничного вывода информации из буфера на экран

{if (UsedN)

{clrscr();

int i=0,ii=1;

while(i<UsedN)

{for(int j=1;j<80;j++)

{gotoxy(j,ii);

printf("%c",str[i][j-1]);

}

ii++;i++;

if (ii==25){getch();

clrscr();

ii=1;}

}

getch();

}}

class WinText:public Window ,public Text //класс «окно для отображения текста»

{ int DeltaX,DeltaY; //величина прокрутки текста в окне по вертикали и горизонтали

public:

WinText(int numb=25,int xb=10,int yb=10,int a=60,int b=20): //конструктор

Window(xb,yb,a,b),Text(numb) //вызов конструкторов базовых классов

{DeltaX=0;DeltaY=0;}

void draw(); //переопределяем функцию отображения текста так, чтобы текст

//отображался в окне

char Control(); //функция, реализующая реакцию на нажатия клавиш

};

void WinText::draw()

{ wind::draw();

textcolor(color);

for(int i=0;i<dy&&i+DeltaY<UsedN;i++)

for (int j=0;j<dx;j++)

{gotoxy(j+y,i+x);

printf("%c",str[i+DeltaY][j+DeltaX]); //отображаем текст в окне с учетом прокрутки

}}

char WinText::Control()

{ char ch;

ch=getch();

if (!ch)

{ ch=getch();

switch(ch) //обработка нажатия клавиш-стрелок

{case 72:if (DeltaY>0) {DeltaY--;draw();}break;

case 80:if (DeltaY<UsedN){DeltaY++;draw();}break;

case 75:if (DeltaX>0) {DeltaX--;draw();}break;

case 77:if (DeltaX<80-dy) {DeltaX++;draw();}

} }

return ch;

}

main()

{textbackground(0);

clrscr();

WinText w(50,2,2,10,15); //определяем объект – окно с буфером на 50 строк

// размером 10 на 15 с координатами верхнего левого угла 2,2

w.Read("lect9.cpp"); //считываем в буфер объекта содержимое файла lect9.cpp

w.draw(); //отображаем содержимое буфера в окне

while(w.Control()!=27); //пока не нажата клавиша ESC, просматриваем текст в окне

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]