Читать интересную книгу Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 135 136 137 138 139 140 141 142 143 ... 337
этого фигура перестанет быть окружностью. Нам не нужен объект, который иногда относится к одному типу (когда major()!=minor()), а иногда к другому (когда major()==minor()). Нам нужен объект (класса Ellipse), который иногда выглядит как окружность. С другой стороны, объект класса Circle никогда не превратится в эллипс с двумя неравными осями.

 

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

 

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

13.14. Класс Marked_polyline

Часто возникает необходимость пометить точки графика. График можно изобразить в виде ломаной, поэтому нам нужна ломаная, точки которой имели бы метки. Для этого предназначен класс Marked_polyline. Рассмотрим пример.

Marked_polyline mpl("1234");

mpl.add(Point(100,100));

mpl.add(Point(150,200));

mpl.add(Point(250,250));

mpl.add(Point(300,200));

В результате выполнения этого фрагмента программы получим следующий результат:

Определение класса Marked_polyline имеет следующий вид:

struct Marked_polyline:Open_polyline {

  Marked_polyline(const string& m):mark(m)

  {

  if (m=="") mark = "*";

  }

  void draw_lines() const;

private:

  string mark;

};

Поскольку этот класс является наследником класса Open_polyline, можем свободно обрабатывать объекты класса Point, и все что нам для этого необходимо — иметь возможность ставить метки. В частности, функция draw_lines() примет следующий вид:

void Marked_polyline::draw_lines() const

{

  Open_polyline::draw_lines();

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

    draw_mark(point(i),mark[i%mark.size()]);

}

Вызов функции Open_polyline::draw_lines() рисует линии, так что остается просто расставить метки. Эти метки представляют собой строки символов, которые используются в определенном порядке: команда mark[i%mark.size()] выбирает символ, который должен быть использован следующим, циклически перебирая символы, хранящиеся в объекте класса Marked_polyline. Оператор % означает деление по модулю (взятие остатка). Для вывода буквы в заданной точке функция draw_lines() использует вспомогательную функцию меньшего размера draw_mark().

void draw_mark(Point xy, char c)

{

  static const int dx = 4;

  static const int dy = 4;

  string m(1,c);

  fl_draw(m.c_str(),xy.x–dx,xy.y+dy);

}

Константы dx и dy используются для центрирования буквы относительно заданной точки. Объект m класса хранит единственный символ c. 

13.15. Класс Marks

Иногда необходимо вывести метки отдельно от линий. Для этого предназначен класс Marks. Например, мы можем пометить четыре точки, использованные в предыдущих примерах, не соединяя их линиями.

Marks pp("x");

pp.add(Point(100,100));

pp.add(Point(150,200));

pp.add(Point(250,250));

pp.add(Point(300,200));

В итоге будет получено следующее изображение:

Очевидно, что класс Marks можно использовать для отображения дискретных данных, изображать которые с помощью ломаной было бы неуместно. В качестве примера можно привести пары (рост, вес), характеризующие группу людей.

Класс Marks — это разновидность класса Marked_polyline с невидимыми линиями.

struct Marks : Marked_polyline {

  Marks(const string& m) :Marked_polyline(m)

  {

    set_color(Color(Color::invisible));

  }

};

13.16. Класс Mark

Объект класса Point задает координаты в объекте класса Window. Иногда мы их отображаем, а иногда нет. Если возникает необходимость пометить отдельную точку, чтобы ее увидеть, мы можем изобразить ее в виде крестиков, как показано в разделе 13.2, или воспользоваться классом Marks. Это объяснение слегка многословно, поэтому рассмотрим простой объект класса Marks, инициализированный точкой и символом.

Например, мы могли бы пометить центры окружностей, изображенных в разделе 13.12, следующим образом:

Mark m1(Point(100,200),'x');

Mark m2(Point(150,200),'y');

Mark m3(Point(200,200),'z');

c1.set_color(Color::blue);

c2.set_color(Color::red);

c3.set_color(Color::green);

В итоге мы получили бы изображения, приведенные ниже.

Класс Mark — это разновидность класса Marks, в котором при создании объекта немедленно задается начальная (и, как правило, единственная) точка.

struct Mark : Marks {

  Mark(Point xy, char c) : Marks(string(1,c))

  {

    add(xy);

  }

};

Функция string(1,c) — это конструктор класса string, инициализирующий строку, содержащую единственный символ c.

Класс Mark всего лишь позволяет легко создать объект класса Marks с единственной точкой, помеченной единственным символом. Стоило ли тратить силы, чтобы определять такой класс? Или он является следствием “ложного стремления к усложнениям и недоразумениям”? Однозначного и логичного ответа на этот вопрос нет. Мы много думали над этим и в конце концов решили, что для пользователей этот класс был бы полезен, а определить его было совсем нетрудно.

Почему в качестве метки используется символ? Можно было бы нарисовать любую маленькую фигуру, но символы нагляднее и проще. Они часто позволяют отделить одно множество точек от другого. К тому же такие символы, как x, o, + и *, обладают центральной симметрией. 

13.17. Класс Image

Файлы в типичном персональном компьютере хранят тысячи изображений. Кроме того, миллионы изображений доступны в сети веб. Естественно, мы хотели бы отображать содержимое этих файлов на экране с помощью относительно простых программ. Например, ниже продемонстрирован рисунок (rita_path.gif), иллюстрирующий путь урагана “Рита”, пришедшего из Мексиканского залива.

Мы можем выбрать часть этого изображения и добавить фотографию урагана, сделанную из космоса (rita.jpg).

Image rita(Point(0,0),"rita.jpg");

Image path(Point(0,0),"rita_path.gif");

path.set_mask(Point(50,250),600,400); // выбираем желательную область

win.attach(path);

win.attach(rita);

Операция set_mask() выбирает часть рисунка, которую следует изобразить на экране. В данном случае мы выбрали изображение размером 600×400 пикселей из файла rita_path.gif (загруженный как объект path) и показали его в области, левый верхний угол которой имеет координаты (50,250). Выбор части рисунка — довольно распространенный прием, поэтому мы предусмотрели для него отдельную операцию.

1 ... 135 136 137 138 139 140 141 142 143 ... 337
На этом сайте Вы можете читать книги онлайн бесплатно русская версия Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп.
Книги, аналогичгные Программирование. Принципы и практика использования C++ Исправленное издание - Бьёрн Страуструп

Оставить комментарий