Читаем Программирование полностью

Естественно, сначала вектор 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 points; // не используется всеми фигурами

  Color lcolor;

  Line_style ls;

  Color fcolor;

}


Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже