РефератыИнформатика, программированиеЗаЗаконченная программа

Законченная программа

Разберем процесс написания программы для рисования на экране геометрических фигур. Она естественным образом разделяется на три части:


Администратор экрана: подпрограммы низкого уровня и структуры данных, определяющие экран; он ведает только точками и прямыми линиями;


Библиотека фигур: набор определений основных фигур вроде прямоугольника и круга и стандартные программы для работы с ними; и


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


Эти три части скорее всего будут писать разные люди (в разных организациях и в разное время). При этом части будут скорее всего писать именно в указанном порядке с тем осложняющим обстоятельством, что у разработчиков нижнего уровня не будет точного представления, для чего их код в конечном счете будет использоваться. Это отражено в приводимом примере. Чтобы пример был короче, графическая библиотека предоставляет только весьма ограниченный сервис, а сама прикладная программа очень проста. Чтобы читатель смог испытать программу, даже если у него нет совсем никаких графических средств, используется чрезвычайно простая концепция экрана. Не должно составить труда заменить эту экранную часть программы чем-нибудь подходящим, не изменяя код библиотеки фигур и прикладной программы.



Администратор Экрана


Вначале было намерение написать администратор экрана на C (а не на C++), чтобы подчеркнуть разделение уровней реализации. Это оказалось слишком утомительным, поэтому пришлось пойти на компромисс: используется стиль C (нет функций членов, виртуальных функций, определяемых пользователем операций и т.п.), однако применяются конструкторы, надлежащим образом описываются и проверяются параметры функций и т.д. Оглядываясь назад, можно сказать, что администратор экрана очень похож на C программу, которую потом модифицировали, чтобы воспользоваться средствами C++ не переписывая все полностью.


Экран представляется как двумерный массив символов, работу с которым осуществляют функции put_point() и put_line(), использующие при ссылке на экран структуру point:


// файл screen.h


const XMAX=40, YMAX=24;


struct point {


int x,y;


point() {}


point(int a, int b) { x=a; y=b; }


};


overload put_point;


extern void put_point(int a, int b);


inline void put_point(point p) { put_point(p.x,p.y); }


overload put_line;


extern void put_line(int, int, int, int);


inline void put_line(point a, point b)


{ put_line(a.x,a.y,b.x,b.y); }


extern void screen_init();


extern void screen_refresh();


extern void screen_clear();


#include


Перед первым использованием функции put экран надо инициализировать с помощью screen_init(), а изменения в структуре данных экрана отображаются на экране только после вызова screen_refresh(). Как увидит пользователь, это "обновление" ("refresh") осуществляется просто посредством печати новой копии экрана под его предыдущим вариантом. Вот функции и определения данных для экрана:


#include "screen.h"


#include


enum color { black="*", white=" " };


char screen[XMAX][YNAX];


void screen_init()


{


for (int y=0; y=a || a<=b) y0 += dy, eps -= two_a;


}


}


Предоставляются функции для очистки экрана и его обновления:


void screen_clear() { screen_init(); } // очистка


void screen_refresh() // обновление


{


for (int y=YMAX-1; 0<=y; y--) { // сверхувниз


for (int x=0; x


Библиотека Фигур


Нам нужно определить общее понятие фигуры (shape). Это надо сделать таким образом, чтобы оно использовалось (как базовый класс) всеми конкретными фигурами (например, кругами и квадратами), и так, чтобы любой фигурой можно было манипулировать исключительно через интерфейс, предоставляемый классом shape:


struct shape {


shape() { shape_list.append(this); }


virtual point north() { return point(0,0); } // север


virtual point south() { return point(0,0); } // юг


virtual point east() { return point(0,0); } // восток


virtual point neast() { return point(0,0); } // северо-восток


virtual point seast() { return point(0,0); } // юго-восток


virtual void draw() {}; // нарисовать


virtual void move(int, int) {}; // переместить


};


Идея состоит в том, что расположение фигуры задается с помощью move(), и фигура помещается на экран с помощью draw(). Фигуры можно располагать относительно друг друга, используя понятие точки соприкосновения, и эти точки перечисляются после точек на компасе (сторон света). Каждая конкретная фигура определяет свой смысл этих точек, и каждая определяет способ, которым она рисуется. Для экономии места здесь на самом деле определяются только н

еобходимые в этом примере стороны света. Конструктор shape::shape() добавляет фигуру в список фигур shape_list. Этот список является gslist, то есть, одним из вариантов обобщенного односвязанного списка, определенного в #7.3.5. Он и соответствующий итератор были сделаны так:


typedef shape* sp;


declare(gslist,sp);


typedef gslist(sp) shape_lst;


typedef gslist_iterator(sp) sp_iterator;


поэтому shape_list можно описать так:


shape_lst shape_list;


Линию можно построить либо по двум точкам, либо по точке и целому. В последнем случае создается горизонтальная линия, длину которой определяет целое. Знак целого указывает, каким концом является точка: левым или правым. Вот определение:


class line : public shape {


/*


линия из "w" в "e"


north() определяется как ``выше центра


и на север как до самой северной точки""


*/


point w,e;


public:


point north()


{ return point((w.x+e.x)/2,e.ydraw();


screen_refresh();


}


И вот, наконец, настоящая сервисная функция (утилита). Она кладет одну фигуру на верх другой, задавая, что south() одной должен быть сразу над north() другой:


void stack(shape* q, shape* p) // ставит p наверх q


{


point n = p->north();


point s = q->south();


q->move(n.x-s.x,n.y-s.y+1);


}


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


Прикладная Программа


Прикладная программа чрезвычайно проста. Определяется новая фигура my_shape (на печати она немного похожа на рожицу), а потом пишется главная программа, которая надевает на нее шляпу. Вначалеописание my_shape:


#include "shape.h"


class myshape : public rectangle {


line* l_eye; // левыйглаз


line* r_eye; // правыйглаз


line* mouth; // рот


public:


myshape(point, point);


void draw();


void move(int, int);


};


Глаза и рот - отдельные и независимые объекты, которые создает конструктор my_shape:


myshape::myshape(point a, point b) : (a,b)


{


int ll = neast().x-swest().x+1;


int hh = neast().y-swest().y+1;


l_eye = new line(


point(swest().x+2,swest().y+hh*3/4),2);


r_eye = new line(


point(swest().x+ll-4,swest().y+hh*3/4),2);


mouth = new line(


point(swest().x+2,swest().y+hh/4),ll-4);


}


Объекты глаза и рот порознь рисуются заново функцией shape_refresh(), и в принципе могут обрабатываться независимо из объекта my_shape, которому они принадлежат. Это один способ определять средства для иерархически построенных объектов вроде my_shape. Другой способ демонстрируется на примере носа. Никакой нос не определяется, его просто добавляет к картинке функция draw():


void myshape::draw()


{


rectangle::draw();


put_point(point(


(swest().x+neast().x)/2,(swest().y+neast().y)/2));


}


my_shape передвигается посредством перемещения базового прямоугольника rectangle и вторичных объектов l_eye, r_eye и mouth (левого глаза, правого глаза и рта):


void myshape::move()


{


rectangle::move();


l_eye->move(a,b);


r_eye->move(a,b);


mouth->move(a,b);


}


Мы можем, наконец, построить несколько фигур и немного их подвигать:


main()


{


shape* p1 = new rectangle(point(0,0),point(10,10));


shape* p2 = new line(point(0,15),17);


shape* p3 = new myshape(point(15,10),point(27,18));


shape_refresh();


p3->move(-10,-10);


stack(p2,p3);


stack(p1,p2);


shape_refresh();


return 0;


}


Еще раз обратите внимание, как функции вроде shape_refresh() и stack() манипулируют объектами типов, определяемых гораздо позже, чем были написаны (и, может быть, откомпилированы) сами эти функции.


Результатом работы программы будет:




***********


* *


* *


* *


* *


* *


* *


* *


* *


* *


***********


*****************


*************


* *


* ** ** *


* *


* * *


* *


* ********* *


* *


*************


Сохранить в соц. сетях:
Обсуждение:
comments powered by Disqus

Название реферата: Законченная программа

Слов:1174
Символов:10675
Размер:20.85 Кб.