Обратите внимание на то, что если бы мы объявили функцию
f
так, чтобы она получала константную ссылку на объект класса
Window
(как было рекомендовано в разделе 8.5.6), то компилятор предотвратил бы ошибку: мы не можем выполнить вызов
attach(r)
с аргументом типа
const Window
, поскольку функция
attach
должна изменить объект класса
Window
, чтобы зарегистрировать связь между ним и объектом
r
.
14.1.4. Изменяемость
Основные
вопросы, на которые следует ответить, проектируя классы, звучат так: кто может модифицировать данные и как он может это делать? Мы должны гарантировать, что изменение состояния объекта будет осуществляться только членами его класса. Именно для этого предназначены разделы
public
и
private
, но мы продемонстрируем еще более гибкий и тонкий механизм, основанный на ключевом слове
protected
. Это значит, что мы не можем просто включить в класс какой-то член, скажем, переменную
label
типа
string
; мы должны также решить, следует ли открыть его для изменений после создания объекта, и если да, то как. Мы должны также решить, должен ли другой код, кроме данного класса, иметь доступ к переменной
Как указано в главе 13, мы решили предотвратить прямой доступ к большинству данных-членов класса. Это дает нам возможность проверять “глупые” значения, например отрицательные радиусы у объектов класса
Circle
. Для простоты реализации мы не проводим полную проверку, поэтому будьте осторожны, работая с числами. Мы отказались от полной и последовательной проверки, желая уменьшить объем кода и понимая, что если пользователь введет “глупое” значение, то ранее введенные данные от этого не пострадают, просто на экране появится искаженное изображение.
Мы интерпретируем экран (т.е. совокупность объектов класса
Window
) исключительно как устройство вывода. Мы можем выводить новые объекты и удалять старые, но никогда не обращаемся к системе за информацией, которую сами не можем извлечь из структур данных, на основе которых строится изображение.
14.2. Класс Shape
Класс
Shape
отражает общее понятие о том, что может изображаться в объекте класса
Window
на экране.
• Понятие, которое связывает графические объекты с нашей абстракцией
Window
, которая в свою очередь обеспечивает связь с операционной системой и физическим экраном.
• Класс, работающий с цветом и стилем, используемыми при рисовании линий. Для этого он хранит члены классов
Line_style
и
Color
(для линий и заполнения).
• Может хранить последовательности объектов класса Point и информацию о том, как их рисовать.
Опытные проектировщики отметят, что класс, обладающий только этими тремя свойствами, может иметь недостаточно
общий характер. Однако мы описываем решение, которое очень далеко от общего.
Сначала опишем полный класс, а затем подробно его обсудим.
class Shape { // работает с цветом и стилем, хранит последователь -
// ность точек
public:
void draw const; // работает с цветом и рисует линии
virtual void move(int dx, int dy); // перемещает фигуры +=dx
// и +=dy
void set_color(Color col);
Color color const;
void set_style(Line_style sty);
Line_style style const;
void set_fill_color(Color col);
Color fill_color const;
Point point(int i) const; // доступ к точкам только для чтения
int number_of_points const;
virtual ~Shape { }
protected:
Shape;
virtual void draw_lines const; // рисует линии
void add(Point p); // добавляет объект p к точкам
void set_point(int i, Point p); // points[i]=p;
private:
vector<Point> points; // не используется всеми фигурами
Color lcolor; // цвет для линий и символов
Line_style ls;
Color fcolor; // заполняет цветом
Shape(const Shape&); // копирующий конструктор
Shape& operator=(const Shape&);
};
Это относительно сложный класс, разработанный для поддержки работы множества графических классов и представления общего понятия о фигуре на экране. Однако в нем всего четыре данных-членов и пятнадцать функций. Более того, эти функции почти все тривиальны, так что мы можем сосредоточиться на вопросах проектирования. В оставшейся части главы мы пройдемся по всем членам шаг за шагом и объясним их роль в классе.
14.2.1. Абстрактный класс
Сначала рассмотрим конструктор класса
Shape
:
protected:
Shape;
который находится в разделе
protected
. Это значит, что его можно непосредственно использовать только в классах, производных от класса
Shape
(используя обозначение
:Shape
). Иначе говоря, класс
Shape
можно использовать только в качестве базы для других классов, таких как
Line
и
Open_polyline
. Цель ключевого слова
protected:
— гарантировать, что мы не сможем создать объекты класса