Выбрать главу

Здесь важно понимать, что при возврате объекта из функции для временного объекта, хранящего возвращаемое значение, будет вызван его деструктор. Поэтому следует избегать возврата объектов в ситуациях, когда это может иметь пагубные последствия. Для решения этой проблемы вместо возврата объекта из функции используется возврат указателя или ссылки на объект. Но это не всегда осуществимо. Еще один способ решения этой проблемы включает использование конструктора копии, которому посвящен следующий раздел.

Создание и использование конструктора копии

Одним из самых важных форматов перегруженного конструктора является конструктор копии. Как было показано в предыдущих примерах, при передаче объекта функции или возврате объекта из функции могут возникать проблемы. В этом разделе вы узнаете, что один из способов избежать этих проблем состоит в определении конструктора копии, который представляет собой специальный тип перегруженного конструктора.

Для начала еще раз сформулируем проблемы, для решения которых мы хотим определить конструктор копии. При передаче объекта функции создается побитовая (т.е. точная) копия этого объекта, которая передается параметру этой функции. Однако возможны ситуации, когда такая идентичная копия нежелательна. Например, если оригинальный объект содержит указатель на выделяемую динамически память, то и указатель, принадлежащий копии, также будет ссылаться на ту же область памяти. Следовательно, если копия внесет изменения в содержимое этой области памяти, эти изменения коснутся также оригинального объекта! Более того, при завершении функции копия будет разрушена (с вызовом деструктора). Это может нежелательным образом сказаться на исходном объекте.

Аналогичная ситуация возникает при возврате объекта из функции. Компилятор генерирует временный объект, который будет хранить копию значения, возвращаемого функцией. (Это делается автоматически, и без нашего на то согласия.) Этот временный объект выходит за пределы области видимости сразу же, как только инициатору вызова этой функции будет возвращено "обещанное" значение, после чего незамедлительно вызывается деструктор временного объекта. Но если этот деструктор разрушит что-либо нужное для выполняемого далее кода, последствия будут печальны.

Конструктор копии позволяет управлять действиями, составляющими процесс создания копии объекта.

В сердцевине рассматриваемых проблем лежит создание побитовой копии объекта. Чтобы предотвратить их возникновение, необходимо точно определить, что должно происходить, когда создается копия объекта, и тем самым избежать нежелательных побочных эффектов. Этого можно добиться путем создания конструктора копии.

Прежде чем подробнее знакомиться с использованием конструктора копии, важно понимать, что в C++ определено два отдельных вида ситуаций, в которых значение одного объекта передается другому. Первой такой ситуацией является присваивание, а второй — инициализация. Инициализация может выполняться тремя способами, т.е. в случаях, когда:

■ один объект явно инициализирует другой объект, как, например, в объявлении;

■ копия объекта передается параметру функции;

■ генерируется временный объект (чаще всего в качестве значения, возвращаемого функцией).

Конструктор копии применяется только к инициализациям. Он не применяется к присваиваниям.

Узелок на память. Конструкторы копии не оказывают никакого влияния на операции присваивания.

Конструктор копии вызывается в случае, когда один объект инициализирует другой.

Вот как выглядит самый распространенный формат конструктора копии.

имя_класса (const имя_класса &obj) {

 // тело конструктора

}

Здесь элемент obj означает ссылку на объект, которая используется для инициализации другого объекта. Например, предположим, у нас есть класс myclass и объект y типа myclass, тогда при выполнении следующих инструкций будет вызван конструктор копии класса myclass.

myclass х = у; // Объект у явно инициализирует объект x

х.func1(у); // Объект у передается в качестве аргумента.

у = func2(); // Объект у принимает объект, возвращаемый функцией.

В первых двух случаях конструктору копии будет передана ссылка на объект у, а в третьем — ссылка на объект, возвращаемый функцией func2().