Чтение онлайн

ЖАНРЫ

Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:

struct Function:Shape {

Function(Fct f,double r1,double r2,Point orig,

int count = 100,double xscale,double yscale); // ошибка

};

Если аргумент имеет значение, заданное по умолчанию, то все последующие аргументы также должны их иметь.

struct Function:Shape {

Function(Fct f,double r1,double r2,Point orig,

int count = 100,double xscale=25,double yscale=25);

};

Иногда угадать удачные значения по умолчанию

легко. Например, для строки хорошим выбором значения по умолчанию будет пустой объект класса
string
, а для вектора — пустой объект класса
vector
. В других ситуациях, например для класса
Function
, правильно выбрать значения по умолчанию значительно сложнее: для этого приходится применять метод проб и ошибок. Помните, что вы не обязаны задавать значения по умолчанию и, если вам трудно это сделать, просто предоставьте пользователю самому задать аргумент.

15.3.2. Новые примеры

Мы добавили еще несколько функций — косинус (

cos
) из стандартной библиотеки и — просто для того, чтобы продемонстрировать, как создать сложную функцию, — косинус с наклоном
x/2
.

double sloping_cos(double x) { return cos(x)+slope(x); }

Результат приведен ниже.

Соответствующий фрагмент кода выглядит так:

Function s4(cos,r_min,r_max,orig,400,20,20);

s4.set_color(Color::blue);

Function s5(sloping_cos, r_min,r_max,orig,400,20,20);

x.label.move(–160,0);

x.notches.set_color(Color::dark_red);

Кроме сложения этих двух функций, мы сместили метку оси x и (просто для иллюстрации) немного изменили цвет шкалы деления.

В заключение построим графики логарифма, экспоненты, синуса и косинуса.

Function f1(log,0.000001,r_max,orig,200,30,30); // ln

Function f2(sin,r_min,r_max,orig,200,30,30); // sin

f2.set_color(Color::blue);

Function f3(cos,r_min,r_max,orig,200,30,30); // cos

Function f4(exp,r_min,r_max,orig,200,30,30); // exp

Поскольку значение

log(0)
не определено (с математической точки зрения оно равно бесконечности), мы начали диапазон изменения функции
log
с небольшого положительного числа. Результат приведен ниже.

Вместо приписывания меток этим графикам мы изменили их цвет.

Стандартные математические функции, такие как

cos
,
sin
и
sqrt
, объявлены в стандартном библиотечном заголовке
<cmath>
. Список стандартных математических функций приведен в разделах 24.8 и B.9.2.

15.4. Оси

Для представления данных мы используем класс

Axis
(например, как в разделе 15.6.4), поскольку график без информации о его масштабе выглядит подозрительно. Класс
Axis
состоит из линии, определенного количества делений оси и текстовой метки. Конструктор класса
Axis
вычисляет координаты линии оси и (при необходимости) линий, используемых как деления оси.

struct Axis:Shape {

enum Orientation { x, y, z };

Axis(Orientation d, Point xy, int length,

int number_of_notches=0, string label = "");

void draw_lines const;

void move(int dx, int dy);

void set_color(Color c);

Text label;

Lines notches;

};

Объекты

label
и
notches
остаются открытыми, поэтому пользователи могут ими манипулировать, например приписывать делениям цвет, отличающийся от цвета линии, или перемещать объект
label
с помощью функции
move
в более удобное место. Объект класса
Axis
— это пример объекта, состоящего из нескольких полунезависимых объектов.

Конструктор класса

Axis
размещает линии и добавляет на них деления, если значение
number_ of_notches
больше нуля.

Axis::Axis(Orientation d, Point xy, int length, int n, string lab)

:label(Point(0,0),lab)

{

if (length<0) error("bad axis length");

switch (d){

case Axis::x:

{

Shape::add(xy); // линия оси

Shape::add(Point(xy.x+length,xy.y));

if (0<n) { // добавляет деления

int dist = length/n;

int x = xy.x+dist;

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

notches.add(Point(x,xy.y),Point(x,xy.y–5));

x += dist;

}

}

label.move(length/3,xy.y+20); // размещает метку под линией

break;

}

case Axis::y:

{ Shape::add(xy); // ось y перемещаем вверх

Shape::add(Point(xy.x,xy.y–length));

if (0<n) { // добавляем деления

int dist = length/n;

int y = xy.y–dist;

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

notches.add(Point(xy.x,y),Point(xy.x+5,y));

y –= dist;

}

}

label.move(xy.x–10,xy.y–length–10); // размещает метку

// наверху

break;

}

case Axis::z:

error("ось z не реализована");

}

}

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

Shape
, унаследованной классом
Axis
(используя функцию
Shape::add
), хотя деления хранятся в виде отдельного объекта (
notches
). Это позволяет нам манипулировать линией и делениями оси независимо друг от друга; например, мы можем раскрасить их в разные цвета. Аналогично метка была помещена в фиксированное положение, но, поскольку она является независимым объектом, мы всегда можем переместить ее в другое место. Для удобства используем перечисление
Orientation
.

Поделиться с друзьями: