Глава 17. Конструкторы: копирование и присваивание 415

82 // Демострация того факта, что 'ouch' и

83 // 'startMeUp' указывают на один и тот же блок памяти

84 for( unsigned int k = 0; k < 10; k++)

85 cout « ouch[k] « endl;

86 }

87 #endif

В строках 36—41 определяется конструктор копий, выполняющий буквальное копирование. Именно так функционирует конструктор копий, который компилятор будет генерировать по умолчанию, если вы не заблокируете копирование (см. далее в этой главе раздел "Блокирование копирования и присваивания") или не определите корректно реализованный конструктор копий.

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

Так что для класса, подобного INT_ARRAY, придется выполнять развернутое копирование или создать специальный механизм для подсчета числа ссылок. Для разработки конструкторов копий и операций присваивания можно сформулировать следующие рекомендации.

• Если класс содержит указатели или ссылки, или использует функции new и delete, то вам следует либо использовать развернутое копирование, либо заблокировать копирование вообще, либо изобрести какой-то механизм подсчета количества ссылок.

• Механизм копирования, используемый в конструкторе копии и в операции присваивания, должен быть одинаков (в разделе "Реализация копирования через присваивание" показано, как этого достичь).

Наконец в функции main() для объекта startMeUp создается искусственная область видимости так, что его деструктор вызывается в строке 81 (раньше, чем деструктор объекта ouch). В строках 76—78 элементам объекта startMeUp присваиваются значения от 0 до 9. Строка 80 содержит вызов операции присваивания, следовательно в обоих объектах элементы будут иметь значения от 0 до 9-

При достижении строки 81 вызывается деструктор объекта startMeUp, освобождающий память, на которую указывает int *data. В правильно определенном классе это не должно затрагивать другие объекты того же типа. Этот класс, однако, использует буквальное копирование там, где требуется развернутое. В результате получается, что указатель data объекта o,uch ссылается неизвестно куда.

Запомните: если достаточно буквального копирования, то удобно использовать копирование и присваивание, генерируемые компилятором. А теперь давайте посмотрим, что такое развернутое копирование и как его следует определять.