, чтобы иметь возможность использовать информацию о цвете, которая в них хранится. Объект класса
Lines
хранится в разделе
Axis::Shape
и использует информацию о цвете, хранящуюся там же.
Мы можем задать цвет линии, деления и метки по отдельности, но с точки зрения красоты стиля этого лучше не делать, а задать их все с помощью одной функции.
void Axis::set_color(Color c)
{
Shape::set_color(c);
notches.set_color(c);
label.set_color(c);
}
Аналогично,
функция
Axis::move
перемещает все три части объекта класса
Axis
одновременно.
void Axis::move(int dx, int dy)
{
Shape::move(dx,dy);
notches.move(dx,dy);
label.move(dx,dy);
}
15.5. Аппроксимация
Рассмотрим еще один небольшой пример построения графика функции: “анимируем” вычисление экспоненты. Наша цель — дать вам почувствовать математические функции, продемонстрировать применение графиков для иллюстрации вычислений, показать фрагменты кода и, в заключение, предупредить о типичных проблемах, связанных с вычислениями.
Один из способов вычисления экспоненты сводится к суммированию степенного ряда.
ex = 1 + x + x2/2! + x3/3! + x4/4! + ...
Чем больше членов ряда мы вычислим, тем точнее будет значение
ex
; иначе говоря, чем больше членов ряда мы вычисляем, тем больше правильных цифр найдем в результате. В программе мы суммируем ряд и строим график его частичных сумм. В этой формуле знак восклицания, как обычно, обозначает факториал, т.е. мы строим графики функций в следующем порядке:
double expe(double x, int n) // сумма n членов для x
{
double sum = 0;
for (int i=0; i<n; ++i) sum+=term(x,i);
return sum;
}
Как построить график этой функции? С точки зрения программиста трудность заключается в том, что наш класс
Function
получает имя функции одного аргумента, а функция
expe
имеет два аргумента. В языке С++ нет элегантного решения этой задачи, поэтому пока воспользуемся неэлегантным решением (тем не менее, см. упр. 3). Мы можем удалить точность
n
из списка аргументов и сделать ее переменной.
int expN_number_of_terms = 10;
double expN(double x)
{
return expe(x,expN_number_of_terms);
}
Теперь функция
expN(x)
вычисляет экспоненту с точностью, определенной значением переменной
expN_number_of_terms
. Воспользуемся этим для построения нескольких графиков. Сначала построим оси и нарисуем истинный график экспоненты, используя стандартную библиотечную функцию
exp
, чтобы увидеть, насколько хорошо она приближается функцией
expN
.
Function real_exp(exp,r_min,r_max,orig,200,x_scale,y_scale);
real_exp.set_color(Color::blue);
Затем выполним цикл приближений, увеличивая количество членов ряда
n
.
for (int n = 0; n<50; ++n) {
ostringstream ss;
ss << " приближение exp; n==" << n ;
win.set_label(ss.str);
expN_number_of_terms = n;
// следующее приближение:
Function e(expN,r_min,r_max,orig,200,x_scale,y_scale);
win.attach(e);
win.wait_for_button;
win.detach(e);
}
Обратите внимание на последний вызов
detach(e)
в этом цикле. Область видимости объекта
e
класса
Function
ограничена телом цикла
for
. Каждый раз, кода мы входим в этот блок, мы создаем новый объект
e
класса
Function
, а каждый раз, когда выходим из блока, объект
e
уничтожается и затем заменяется новым. Объект класса
Window
не должен помнить о старом объекте
e
, потому что он будет уничтожен. Следовательно, вызов