C++. Бархатный путь. Часть 2 - Переопределение конструктора копирования

ОГЛАВЛЕНИЕ

 

Переопределение конструктора копирования

Упомянутая нами в предыдущем разделе аксиома о конструкторе копирования имеет одно интересное следствие.

В классе X в принципе не может быть объявлено конструктора с ЕДИНСТВЕННЫМ параметром типа X. Это происходит из-за того, что выражение вызова такого конструктора просто невозможно будет отличить от выражения вызова конструктора копирования. Не бывает совместно используемых функций с неразличимыми выражениями вызова.

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

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

Мы можем построить собственную версию конструктора копирования. По традиции мы начинаем с ничего не делающего конструктора. Наш новый встроенный конструктор копирования лишь сообщает о собственном присутствии.

ComplexType(const ComplexType& ctVal)
{
cout << "Здесь конструктор копирования" << endl;
} ;
//^ В теле класса ComplexType мы имеем право на эту точку с запятой…

Несмотря на пустое тело, перед нами настоящий конструктор копирования. Всякий конструктор, параметром которого является ссылка на объект-константу, представляющий данный класс, называется конструктором копирования. Даже если этот конструктор ничего не копирует.

Переопределение конструктора копирования является чрезвычайно ответственным поступком. Явное определение конструктора копирования вызывает изменения в работе программы. Пока мы не пытались переопределить конструктор копирования, исправно работал конструктор, порождаемый транслятором. Этот конструктор создавал "фотографические" копии объектов, то есть копировал значения абсолютно всех данных-членов, в том числе и ненулевые значения указателей, представляющие адреса динамических областей памяти. 

С момента появления переопределённой версии конструктора копирования, вся работа по реализации алгоритмов копирования возлагается на программиста. Переопределённый конструктор копирования может вообще ничего не копировать (как и наш новый конструктор). Впрочем, заставить конструктор копирования копировать объекты совсем несложно:

ComplexType(const ComplexType& ctVal)
{
cout << "Здесь конструктор копирования" << endl;
real = ctVal.real;
imag = ctVal.imag;
CTcharVal = ctVal.CTcharVal;
x = ctVal.x;
}

Но конструктор, создающий подобные копии объектов, скорее всего, окажется непригодным для работы с объектами, содержащими указатели или ссылки. Не самым удачным решением является ситуация, при которой данные-члены типа char*, их нескольких объектов, возможно расположенных в различных сегментах памяти, в результате деятельности конструктора копирования настраиваются на один и тот же символьный массив.

В переопределяемом конструкторе копирования (а в классе он может быть только один) можно реализовывать разнообразные алгоритмы распределения памяти. Здесь всё зависит от программиста.