Естественно, сначала вектор points пуст. Мы решили снабдить класс Shape полным функциональным интерфейсом, а не предоставлять функциям-членам классов, производных от класса Shape, прямого доступа к его данным-членам. Одним людям создание функционального интерфейса кажется глупым, поскольку они считают, что недопустимо делать какие-либо данные-члены класса открытыми. Другим наш подход кажется слишком узким, потому что мы не разрешаем членам производных классов прямой доступ к членам базового класса.
Классы, производные от класса Shape, например Circle и Polygon, “понимают”, что означают их точки. Базовый класс Shape этого “не понимает”, он просто хранит точки. Следовательно, производные классы должны иметь контроль над тем, как добавляются точки. Рассмотрим пример.
• Классы Circle и Rectangle не позволяют пользователю добавлять точки, они просто “не видят” в этом смысла. Что такое прямоугольник с дополнительной точкой? (См. раздел 12.7.6.)
• Класс Lines позволяет добавлять любые пары точек (но не отдельные точки; см. раздел 13.3).
• Классы Open_polyline и Marks позволяют добавлять любое количество точек.
• Класс Polygon позволяет добавлять точки только с помощью функции add(), проверяющей пересечения (раздел 13.8).
add() в раздел protected (т.е. сделали ее доступной только для производных классов), чтобы гарантировать, что производные классы смогут управлять добавлением точек. Если бы функция add() находилась в разделе public (т.е. каждый класс мог добавлять точки) или private (только класс Shape мог добавлять точки), то такое точное соответствие функциональных возможностей нашему представлению о фигуре стало бы невозможным.
По аналогичным причинам мы поместили функцию 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
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
Color lcolor;
Line_style ls;
Color fcolor;
}