Функция
add()
использует механизм передачи параметров по значению (копии), а функция
attach()
— механизм передачи параметров по ссылке (использует общий объект). Мы могли бы решить копировать графические объекты в объекты класса
Window
. Однако это была бы совсем другая модель программирования, которая определяется выбором функции
add()
, а не
attach()
. Мы решили просто связать графический объект с объектом класса
Window
. Это решение имеет важные последствия. Например, мы не можем создать объект, связать его, позволить его уничтожить и ожидать, что программа продолжит работать.
void f(Simple_window& w)
{
Rectangle r(Point(100,200),50,30);
w.attach(r);
} // Ой, объекта r больше нет
int main()
{
Simple_window win(Point(100,100),600,400,"Мое окно");
// ...
f(win); // возникают проблемы
// ...
win.wait_for_button();
}
Пока мы выходили из функции
f()
и входили в функцию
wait_for_button()
, объект
r
для объекта win перестал существовать и соответственно выводиться на экран. В главе 17 мы покажем, как создать объект в функции и сохранить его между ее вызовами, а пока должны избежать связывания с объектом, который исчез до вызова функции
wait_for_button()
. Для этого можно использовать класс
Vector_ref
, который рассматривается в разделах 14.10 и Г.4.
Обратите внимание на то, что если бы мы объявили функцию
f()
так, чтобы она получала константную ссылку на объект класса
Window
(как было рекомендовано в разделе 8.5.6), то компилятор предотвратил бы ошибку: мы не можем выполнить вызов
attach(r)
с аргументом типа
const Window
, поскольку функция
attach()
должна изменить объект класса
Window
, чтобы зарегистрировать связь между ним и объектом
r
.
14.1.4. Изменяемость
Основные вопросы, на которые следует ответить, проектируя классы, звучат так: кто может модифицировать данные и как он может это делать? Мы должны гарантировать, что изменение состояния объекта будет осуществляться только членами его класса. Именно для этого предназначены разделы
public
и
private
, но мы продемонстрируем еще более гибкий и тонкий механизм, основанный на ключевом слове
protected
. Это значит, что мы не можем просто включить в класс какой-то член, скажем, переменную
label
типа
string
; мы должны также решить, следует ли открыть его для изменений после создания объекта, и если да, то как. Мы должны также решить, должен ли другой код, кроме данного класса, иметь доступ к переменной
label
, и если да, то как. Рассмотрим пример.
struct Circle {
// ...
private:
int r; // radius
};
Circle c(Point(100,200),50);
c.r = –9; // OK? Нет — ошибка компилирования: переменная Circle::r
// закрыта
Как указано в главе 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;