Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

OOP / Лекция 5

.pdf
Скачиваний:
25
Добавлен:
20.04.2015
Размер:
204.29 Кб
Скачать

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:

 

 

строк в буфере, UsedN – количество

реально

int n,UsedN; //n-количество

используемых

 

 

 

 

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

 

 

 

char **str;

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

 

public:

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

 

 

Text(int );

 

 

~Text();

//деструктор

//функция чтения информации из

файла в

void Read(char *filename);

буфер

 

//функция вывода информации из буфера на экран

 

void Write();

 

};

 

//конструктор динамически выделяет память под k

Text::Text(int k)

строк…

 

 

 

 

{ n=k;UsedN=0;

 

 

 

 

str=new char*[n];

 

 

 

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

//…в каждой строке 80 символов

 

str[i]=new char[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:

numb=25,int

xb=10,int

yb=10,int

a=60,int

b=20):

WinText(int

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

 

 

 

 

 

 

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();

 

//определяем

объект – окно с буфером на 50

WinText w(50,2,2,10,15);

строк

 

 

 

 

 

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

файла

w.Read("lect9.cpp");

//считываем в

буфер объекта

содержимое

lect9.cpp

//отображаем содержимое буфера в окне

 

 

w.draw();

 

 

4.Виртуальные классы

Внекоторых случаях дублирование компонент непрямого базового класса необходимо устранить. Например, перед нами стоит задача определить классы, описывающие поведение шахматных фигур.

//Листинг 27. Определение классов «шахматные фигуры»

class Figure

//класс «фигура»

{ protected:

//позиция фигуры по горизонтали

int hor;

char vert;

//позиция фигуры по вертикали

int color;

//цвет фигуры

public:

Figure(char x, int y, int z) //конструктор

: vert(x), hor(y), color(z)

 

{}

 

 

};

 

//класс ладья

class Castle : public Figure

{ public:

 

 

Сastle(char x, int y, int z):

 

Figure (x, y, z)

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

{}

int y)

//функция, реализующая ход ладьи на поле [x

int Move(char x,

y ]

{if ( ((x == vert)

&&(y != hor)) || ((x != vert)

&&(y == hor)))

{

hor = y; vert = x; return 1;

}

 

 

return 0;

 

 

}

 

 

};

 

//класс слон

class Bishop : public Figure

{ public:

 

 

Bishop(char x, int y, int z):

 

Figure (x, y, z)

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

{}

int y)

//функция, реализующая ход слона на поле [x

int Move(char x,

y ]

{ if (abs((x - vert)

==abs(y - hor))

&&(x != vert)) { vert= x; y=hor; return 1;

}

return 0;

}

};

class Queen: public Bishop, public Castle

{

public:

Queen(char x, int y, int z): //конструктор Castle (x, y, z), Bishop (x, y, z)

{}

int Move(char x, int y) {

return Castle::Move(x,y) || Bishop::Move (x,y); }};

В программе определены 4 класса. Класс Figure является абстрактным обобщением свойств всех шахматных фигур, поэтому он содержит такие компонентные данные, как позиция фигуры на доске, определяемая по вертикали буквой vert и по

горизонтали цифрой hor, а также цвет фигуры color. Классы Castle и Bishop описывают, соответственно, ладью и слона. Для этих классов определена функция

int Move(char x, int y),

проверяющая, может ли данная фигура пойти на поле с указанными в параметрах функции координатами, и если может – сделать этот ход. Самым интересным классом является класс Queen, описывающий поведение ферзя. Каждый, кто знаком с правилами шахмат, знает, что ферзь объединяет в себе свойства ладьи и слона (в том смысле, что может ходить как по диагонали, как слон, так и по

Рисунок 10. - Схема иерархии классов программы «Шахматы»

вертикали и горизонтали, как ладья). Поэтому класс Queen объявлен потомком двух классов: Castle и Bishop. Таким образом, имеется иерархия классов, изображенная на рис.10.

Приведенный пример реализации класса Queen содержит ошибку. Дело в том, что для класса Queen дублируются компонентные данные vert, hor и color, в то время как у реального ферзя всего одна позиция на доске и один цвет. Таким образом, встала задача предотвратить дублирование компонент непрямого базового класса в производном. Решить эту проблему можно, объявив класс Figure виртуальным. Для того, чтобы определить непрямой базовый класс виртуальным, необходимо при объявлении этого класса базовым в списке порождения указать ключевое слово virtual. Спецификатор virtual способствует минимизации структуры производного класса. Главная особенность виртуальных базовых классов - они не тиражируются. Изменим определение классов шахматных фигур.

class Figure {…};

class Castle: public virtual Figure {…}; class Bishop: public virtual Figure {…}; class Castle: public Castle, public Bishop{…};

Теперь схема иерархии классов выглядит так, как показано на рис.11, и компонентные данные vert, hor, color не будут дублироваться в объектах класса Queen.

Рисунок 11. - При использовании виртуальных классов схема иерархии классов принимает ромбовидную форму

При использовании виртуальных классов необходимо обратить внимание на особенность вызова конструкторов базовых классов. Конструктор класса Queen был определен следующим образом:

Queen(char x, int y, int z): Castle (x, y, z), Bishop (x, y, z) {}

В конструкторе предусмотрен вызов конструкторов прямых базовых классов, которые создают в памяти экземпляры классов Castle и Bishop . В свою очередь, конструкторы классов Castle и Bishop вызывают конструктор своего базового класса (Figure), вследствие чего и создавалось два экземпляра класса Figure. Если класс Figure объявлен виртуальным, конструктор класса Queen необходимо переопределить:

Queen(char x, int y, int z): Castle (x, y, z), Bishop (x, y, z), Figure(x,y,z)

{}

Если Figure – виртуальный класс, то конструкторы классов Castle и Bishop не будут вызывать конструктор класса Figure, а для того, чтобы один экземпляр этого объекта все таки был создан, вызов конструктора класса Figure необходимо поместить непосредственно в определение класса Queen. При множественном наследовании существует возможность определять один и тот же базовый класс как косвенный базовый для другого класса несколько раз, причем и как виртуальный, и как невиртуальный. Рассмотрим следующий пример:

class A {…};

class B: virtual public A {…}; class C: virtual public A {…}; class D: public A{…};

class E: public A {…};

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

Схема иерархии для такой системы классов в графической форме приведена на рис.12.

Рисунок 12. - Использование одного и того же класса как виртуальной, так и невиртуальной базы

В данном случае объект класса F будет включать три экземпляра класса A: один виртуальный, совместно используемый классами B и С, и два невиртуальных, относящихся к классам E и D.

Соседние файлы в папке OOP