Программирование. Принципы и практика использования C++ Исправленное издание
Шрифт:
По аналогичным причинам мы поместили функцию
set_point
в класс protected
. В общем, только производный класс может “знать”, что означают точки и можно ли их изменять, не нарушая инвариант. Например, если класс
Regular_hexagon
объявлен как множество, состоящее из шести точек, то изменение даже одной точки может породить фигуру, не являющуюся правильным шестиугольником. С другой стороны, если мы изменим одну из точек прямоугольника, то в результате все равно получим прямоугольник. Фактически функция set_point
в этом случае оказывается ненужной, поэтому мы включили ее просто для того, чтобы обеспечить выполнение правил чтения и записи каждого атрибута
Shape
. Например, если бы мы захотели создать класс Mutable_rectangle
, то могли бы вывести его из класса Rectangle
и снабдить операциями, изменяющими точки. Мы поместили вектор
points
объектов класса Point
в раздел private
, чтобы защитить его от нежелательных изменений. Для того чтобы он был полезным, мы должны обеспечить доступ к нему.
void Shape::set_point(int i, Point p) // не используется
{
points[i] = p;
}
Point Shape::point(int i) const
{
return points[i];
}
int Shape::number_of_points const
{
return points.size;
}
В производном классе эти функции используются так:
void Lines::draw_lines const
// рисует линии, соединяющие пары точек
{
for (int i=1; i<number_of_points; i+=2)
fl_line(point(i–1).x,point(i–1).y,point(i).x,point(i).y);
}
number_of_points
занимает столько же байтов памяти и выполняет точно столько же инструкций, сколько и непосредственный вызов функции points.size
. Решения, касающиеся управления доступом, очень важны. Теперь мы могли бы создать почти минимальную версию класса
Shape
.
struct Shape { // слишком простое определение — не используется
Shape;
void draw const; // работает с цветом и вызывает функцию
// draw_lines
virtual void draw_lines const; // рисует линии
virtual void move(int dx, int dy); // перемещает фигуры +=dx
// и +=dy
vector<Point> points; // не используется всеми фигурами
Color lcolor;
Line_style ls;
Color fcolor;
}
private:
и protected:
)? Главный ответ состоит в том, что защита класса от нежелательного изменения позволяет разработчику создавать лучшие классы с меньшими усилиями. Этот же аргумент относится и к инвариантам (см. раздел 9.4.3). Подчеркнем эти преимущества на примере определения классов, производных от класса Shape
. В более ранних вариантах класса Shape
мы использовали следующие переменные:
Fl_Color lcolor;
int line_style;
Оказывается, это очень ограничивает наши возможности (стиль линии, задаваемый переменной типа
int
, не позволяет элегантно задавать ширину линии, а класс Fl_Color
не предусматривает невидимые линии) и приводит к довольно запутанному коду. Если бы эти две переменные были открытыми и использовались в пользовательской программе, то мы могли бы улучшить интерфейсную библиотеку только за счет взлома этого кода (поскольку в нем упоминаются имена lcolor
и line_style
).
s.add(p)
читается и записывается легче, чем s.points.push_back(p)
. 14.2.3. Рисование фигур
Мы описали почти все, кроме ядра класса
Shape
.
void draw const; // работает с цветом и вызывает функцию
// draw_lines
virtual void draw_lines const; // рисует линии
Основная задача класса
Shape
— рисовать фигуры. Мы не можем удалить из класса Shape
все остальные функции и оставить его вообще без данных о нем самом, не нанеся вреда нашей основной концепции (см. раздел 14.4); рисование — это главная задача класса Shape
. Он выполняет ее с помощью библиотеки FLTK и операционной системы, но с точки зрения пользователя он выполнят только две функции. • Функция
draw
интерпретирует стиль и цвет, а затем вызывает функцию draw_lines
. • Функция
draw_lines
подсвечивает пиксели на экране. Функция
draw
не использует никаких новаторских методов. Она просто вызывает функции библиотеки FLTK, чтобы задать цвет и стиль фигуры, вызывает функцию draw_lines
, чтобы выполнить реальное рисование на экране, а затем пытается восстановить цвет и фигуру, заданные до ее вызова.
void Shape::draw const
{
Fl_Color oldc = fl_color;
// универсального способа идентифицировать текущий стиль
// не существует
fl_color(lcolor.as_int); // задаем цвет
fl_line_style(ls.style,ls.width); // задаем стиль
draw_lines;
fl_color(oldc); // восстанавливаем цвет (предыдущий)
fl_line_style(0); // восстанавливаем стиль линии (заданный
Поделиться с друзьями: