Mark m(Point(100,200),'x'); // отображает отдельную точку
// в виде буквы "x"
Circle c(Point(200,200),250);
Все функции, работающие с точками, используют класс
Point
. Это очевидно, но многие библиотеки смешивают стили. Например, представим себе функцию, рисующую линию. Мы можем использовать два стиля.
void draw_line(Point p1,Point p2); // от p1 до p2 (наш стиль)
void draw_line(int x1,int y1,int x2,int y2); //
от (x1,y1)
// до (x2,y2)
Можно было бы допустить оба стиля, но для обеспечения логичности, улучшения проверки типов и повышения читабельности будем пользоваться исключительно первым. Последовательное использование класса
Point
позволит также избежать путаницы между парами координат и другими парами целых чисел: шириной и высотой. Рассмотрим пример.
draw_rectangle(Point(100,200),300,400); // наш стиль
draw_rectangle (100,200,300,400); // альтернатива
При первом вызове функция рисует прямоугольник по заданной точке, ширине и высоте. Это легко угадать. А что можно сказать о втором вызове? Имеется в виду прямоугольник, определенный точками (100,200) и (300,400)? Или прямоугольник, определенный точкой (100,200), шириной 300 и высотой 400? А может быть, программист имел в виду нечто совершенно другое (хотя и разумное)? Последовательно используя класс
Point
, мы можем избежать таких недоразумений.
Иногда, когда функция требует ширину и высоту, они передаются ей именно в таком порядке (как, например, координату x всегда указывают до координаты y). Последовательное соблюдение таких условностей удивительно облегчает работу с программой и позволяет избежать ошибок во время ее выполнения.
Логически идентичные операции называются одинаково. Например, каждая функция, которая добавляет точки, линии и так далее к любой фигуре, называется
add
, а любая функция, рисующая линии, называется
draw_lines
. Такое единообразие позволяет нам помнить (или вспомнить по некоторым признакам), что делает функция, и облегчает разработку новых классов (по аналогии). Иногда это позволяет даже написать код, работающий с разными типами, поскольку операции над этими типами имеют общий шаблон.
Такие коды называют обобщенными (generic); подробно мы рассмотрим их в главах 19–21.
14.1.3. Именование
Логически разные операции имеют разные имена. И опять-таки, несмотря на то, что это очевидно, существуют вопросы: почему мы связываем объект класса
Shape
с объектом класса
Window
, но добавляем объект класса
Line
к объекту класса
Shape
? В обоих случаях мы “помещаем нечто во что-то”, так почему бы не назвать такие операции одинаково? Нет. За этой схожестью кроется фундаментальная разница. Рассмотрим пример.
Open_polyline opl;
opl.add(Point(100,100));
opl.add(Point(150,200));
opl.add(Point(250,250));
Здесь мы копируем три точки в объект
opl
. Фигуре
opl
безразлично,
что будет с нашими точками после вызова функции
add
; она хранит свои собственные копии этих точек. На самом деле мы редко храним копии точек, а просто передаем их фигуре. С другой стороны, посмотрим на следующую инструкцию:
win.attach(opl);
Здесь мы создаем связь между окном win и нашей фигурой
opl
; объект
win
не создает копию объекта
opl
, а вместо этого хранит ссылку на него. Итак, мы должны обеспечить корректность объекта
opl
, поскольку объект
win
использует его. Иначе говоря, когда окно
win
использует фигуру
opl
, оно должно находиться в ее области видимости. Мы можем обновить объект
opl
, и в следующий раз объект
win
будет рисовать фигуру
opl
с изменениями. Разницу между функциями
attach
и
add
можно изобразить графически.
Функция
add
использует механизм передачи параметров по значению (копии), а функция
attach
— механизм передачи параметров по ссылке (использует общий объект). Мы могли бы решить копировать графические объекты в объекты класса
Window
. Однако это была бы совсем другая модель программирования, которая определяется выбором функции
add
, а не
attach
. Мы решили просто связать графический объект с объектом класса
Window
. Это решение имеет важные последствия. Например, мы не можем создать объект, связать его, позволить его уничтожить и ожидать, что программа продолжит работать.
для объекта win перестал существовать и соответственно выводиться на экран. В главе 17 мы покажем, как создать объект в функции и сохранить его между ее вызовами, а пока должны избежать связывания с объектом, который исчез до вызова функции