►"Мелкие " и "глубокие " копии...217
Выполнение поэлементного копирования — естественная задача конструктора копирования. Но что ещё можно сделать с помощью такого конструктора? Когда наконец можно будет попытаться сделать что-то поинтереснее, чем программирование поэлементного копирования и объединения каких-то строк с именем несуществующего студента?
_________________
217 стр. Глава 18. Копирующий конструктор
Представим ситуацию, когда конструктор распределяет для объекта некоторые системные ресурсы, например память из кучи. Если копирующий конструктор будет выполнять простое копирование без выделения памяти из кучи для копируемого объекта, может возникнуть ситуация, когда два объекта будут считать, что именно они являются владельцами одного блока памяти. Ситуация ещё более усугубится при вызове деструкторов обоих объектов, которые попытаются освободить одну и ту же память. Взгляните на приведённый ниже пример.
/* ShallowCopy — мелкое копирование */
/* неприменимо при захвате */
/* ресурсов */
//
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string.h>
using namespace std ;
class Person
{
public :
Person( char *pN )
{
cout << "Конструирование \" " << pN << " \" " << endl ;
pName = new char[ strlen( pN ) + 1 ] ;
if ( pName != 0 )
{
strcpy( pName , pN ) ;
}
}
~Person( )
{
cout << "Деструкция \" " << pName << " \" " << endl ;
strcpy( pName , "Уже освобождённая память" ) ;
/* delete pName ; */
}
protected :
char *pName ;
} ;
void fn( )
{
/* Создание нового объекта */
Person p1( "Достаточно длинное имя" ) ;
/* Копирование p1 в р2 */
Person p2(p1);
}
int main( int argcs , char* pArgs[ ] )
{
setlocale ( LC_ALL , ".1251" ) ; /* печать кириллицы */
cout << "Вызов fn( )" << endl ;
fn( ) ;
cout << "Возврат из fn( )" << endl ;
/* Пауза для того, чтобы посмотреть на результат работы программы */
system( "PAUSE" ) ; return 0 ;
}
_________________
218 стр. Часть 3. Введение в классы
Эта программа порождает следующий вывод:
Вызов fn( )
Конструирование "Достаточно длинное имя"
Деструкция "Достаточно длинное имя"
Деструкция "Уже освобождённая память"
Возврат из fn( )
Press any key to continue...
В этом примере конструктор для Person выделяет память из кучи для хранения в ней имени произвольной длины, что невозможно при использовании массивов. Деструктор возвращает эту память в кучу. Основная программа вызывает функцию fn( ), которая создаёт объект p1, описывающий человека, после чего создаётся копия этого объекта — р2. Оба объекта автоматически уничтожаются при выходе из функции fn( ).
После запуска этой программы вы получите сообщение только от одного конструктора. Это неудивительно, поскольку копия р2 создаётся с помощью предоставляемого С++ конструктора копирования по умолчанию, а он не выводит никаких сообщений. Однако, после того как p1 и р2 выходят из области видимости, вы не получите двух сообщений о ликвидации объектов, как можно было ожидать. Первый конструктор выводит ожидаемое сообщение о деструкции объекта, но второй деструктор сообщает, что память уже была освобождена.