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

В результате только члены класса Shape могут копировать объекты класса Shape, используя операции копирования, заданные по умолчанию. Это общая идиома, предотвращающая непредвиденное копирование. Рассмотрим пример.


void my_fct(const Open_polyline& op, const Circle& c)

{

  Open_polyline op2 = op; // ошибка: копирующий конструктор

                          // класса Shape закрыт

  vector v;

  v.push_back(c);         // ошибка: копирующий конструктор

                          // класса Shape закрыт

  // ...

  op = op2;               // ошибка: присваивание в классе

  // Shape закрыто


  Однако копирование может быть полезным во многих ситуациях! Просто взгляните на функцию push_back(); без копирования было бы трудно использовать векторы (функция push_back() помещает в вектор копию своего аргумента). Почему надо беспокоиться о непредвиденном копировании? Если операция копирования по умолчанию может вызывать проблемы, ее следует запретить. В качестве основного примера такой проблемы рассмотрим функцию my_fct(). Мы не можем копировать объект класса Circle в вектор v, содержащий объекты типа Shape; объект класса Circle имеет радиус, а объект класса Shape — нет, поэтому sizeof(Shape) . Если бы мы допустили операцию v.push_back(c), то объект класса Circle был бы “обрезан” и любое последующее использование элемента вектора v привело бы к краху; операции класса Circle предполагают наличие радиуса (члена r), который не был скопирован.



Конструктор копирования объекта op2 и оператор присваивания объекту op имеют тот же самый недостаток. Рассмотрим пример.


Marked_polyline mp("x");

Circle c(p,10);

my_fct(mp,c); // аргумент типа Open_polyline ссылается

              // на Marked_polyline


Теперь операции копирования класса Open_polyline приведут к “срезке” объекта mark, имеющего тип string.

  В принципе иерархии классов, механизм передачи аргументов по ссылке и копирование по умолчанию не следует смешивать. Разрабатывая базовый класс иерархии, заблокируйте копирующий конструктор и операцию копирующего присваивания, как мы сделали в классе Shape.

Срезка (да, это технический термин) — не единственная причина, по которой следует предотвращать копирование. Существует еще несколько понятий, которые лучше представлять без операций копирования. Напомним, что графическая система должна помнить, где хранится объект класса Shape на экране дисплея. Вот почему мы связываем объекты класса Shape с объектами класса Window, а не копируем их. Объект класса Window ничего не знает о копировании, поэтому в данном случае копия действительно хуже оригинала. 

  Если мы хотим скопировать объекты, имеющие тип, в котором операции копирования по умолчанию были заблокированы, то можем написать явную функцию, выполняющую это задание. Такая функция копирования часто называется clone(). Очевидно, что функцию clone() можно написать, только если функций для чтения данных достаточно для реализации копирования, как в случае с классом Shape

14.3. Базовые и производные классы

Посмотрим на базовый и производные классы с технической точки зрения; другими словами, в этом разделе предметом дискуссии будет не программирование, проектирование и графика, а язык программирования. Разрабатывая нашу библиотеку графического интерфейса, мы использовали три основных механизма.

• Вывод. Это способ построения одного класса из другого так, чтобы новый класс можно было использовать вместо исходного. Например, класс Circle является производным от класса Shape, иначе говоря, класс Circle является разновидностью класса Shape или класс Shape является базовым по отношению к классу Circle. Производный класс (в данном случае Circle) получает все члены базового класса (в данном случае Shape) в дополнение к своим собственным. Это свойство часто называют наследованием (inheritance), потому что производный класс наследует все члены базового класса. Иногда производный класс называют подклассом (subclass), а базовый — суперклассом (superclass).

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