Главная · Поиск книг · Поступления книг · Top 40 · Форумы · Ссылки · Читатели

Настройка текста
Перенос строк


    Прохождения игр    
Stoneshard |#11| Battle at the castle
Stoneshard |#10| A busy reaper
The Elder Scrolls IV: Oblivion Remastered - Trash review
Stoneshard |#9| A Million Liches

Другие игры...


liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня
Rambler's Top100
Образование - Страустрап Б. Весь текст 579.17 Kb

Язык С++

Предыдущая страница Следующая страница
1 ... 28 29 30 31 32 33 34  35 36 37 38 39 40 41 ... 50
  Имя name  может одновременно находиться только в одном olist. Для
имен это  может быть  и неподходит,  но в  классах, для которых это
подойдет полностью,  недостатка нет.  Например, класс  фигур  shape
использует для  поддержки списка  всех  фигур  именно  этот  метод.
Обратите  внимание,   что  можно   было  бы  определить  slist  как
производный от  olist, объединяя  таким образом оба понятия. Однако
использование   бызовых    и   производных    классов   на    таком
микроскопическом уровне может очень сильно исказить код.

     7.5 Неоднородные Списки

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

                             - стр 221 -

     7.6 Законченна Программа

  Разберем процесс  написания программы  для  рисования  на  экране
геметрических фигур.  Она естественным  образом разделяется  на три
части:
  [1] Администратор экрана: подпрограммы низкого уровня и структуры
     данных, определяющие экран; он ведает только точками и прямыми
     линиями;
  [2] Библиотека  фигур: набор  определений оосновных  фигур  вроде
     прямоугольника и  круга и  стандартные программы  для работы с
     ними; и
  [3]     Прикладная      программа:     множество     определений,
     специализированных для  данного приложения,  и код,  в котором
     они используются.
  Эти три  части скорее  всего будут  писать разные  люди (в разных
организациях и  в разное  время). При этом части будут скорее всего
писать   именно    в   указанном    порядке   с   тем   осложняющим
обстоятельством,  что  у  разработчиков  нижнего  уровня  не  будет
точного представления,  для чего  их код  в  конечном  счете  будет
использоваться. Это отражено в приводимом примере. Чтобы пример был
короче,  графическая   библиотека   предоставляет   только   весьма
ограниченный сервис,  а сама  прикладная  программа  очень  проста.
Чтобы читатель смог испытать программу, даже если у него нет совсем
никаких  графических   средств,  используется  чрезвычайно  простая
концепция экрана.  Не должно  составить труда заменить эту экранную
часть программы  чем-нибудь подходящим,  не изменяя  код библиотеки
фигур и прикладной программы.

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

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

                             - стр 222 -

  // файл 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
7.6.2 Библиотека Фигур

  Нам нужно  определить общее  понятие  фигуры  (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;

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

                             - стр 225 -

  class line : public shape {
  /*
      линия из 'w' в 'e'
      north() определяется как ``выше центра
      и на север как до самой северной точки''
  */
      point w,e;
  public:
      point north()
          { return point((w.x+e.x)/2,e.ydraw();
      screen_refresh();
  }

                             - стр 227 -

И вот,  наконец, настоящая  сервисная функция (утилита). Она кладет
одну фигуру  на верх другой, задавая, что 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);
  }

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

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

  Прикладная  программа   чрезвычайно  проста.  Определяется  новая
фигура 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(), и  в принципе  могут обрабатываться  независимо из

                             - стр 228 -

объекта  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() манипулируют  объектами типов,  определяемых гораздо позже,
чем  были  написаны  (и,  может  быть,  откомпилированы)  сами  эти
функции.

                             - стр 229 -

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

     7.7 Свободная Память

  Если вы  пользоались классом slist, вы могли обнаружить, что ваша
программа тратит  на заметное  время на  размещение и  освобождение
объектов класса  slink.  Класс  slink  -  это  превосходный  пример
Предыдущая страница Следующая страница
1 ... 28 29 30 31 32 33 34  35 36 37 38 39 40 41 ... 50
Ваша оценка:
Комментарий:
  Подпись:
(Чтобы комментарии всегда подписывались Вашим именем, можете зарегистрироваться в Клубе читателей)
  Сайт:
 
Комментарии (4)

Реклама