|
Программирование >> Аргументация конструирования
Создаем нового студента Randy Создаем копию от Randy Создаем учителя Вызываем функцию fп() Создаем копию от Копия Randy Это сообщение из функции fп() Ликвидируем Копия Копия Randy Вернулись из функции fп() Ликвидируем Копия Randy Ликвидируем Randy Конструирование объекта randy приводит к вызову конструктора кото- рый выводит первое сообщение. Объект tutor создается с использованием конструктора Tutor (Students). Этот конструктор инициадизирует чден Tutor: : student, вызывая копирующий конструктор для Он и выводит вторую строку с сообщением. Вызов функции fn() требует создания копии tutor. Поскольку я не обеспечил Tutor копирующим конструктором, каждый член объекта tutor копируется посредством конструктора, созданного C++ по умолчанию. При этом, как отмечалось ранее, ддя копирования члена tutor. student вызывается конструктор копирования класса Student. Мемсие и zMfSotcue fconuu Выполнение поэлементного копирования - естественная задача конструктора копирования. Но что еще можно сделать с помощью такого конструктора? Когда наконец можно попытаться сделать что-то поинтереснее, чем программирование поэлементного копирования и объединения каких-то строк с именем несуществующего студента? Представим ситуацию, когда конструктор распределяет объекту некоторые системные ресурсы, например память из кучи. Если копирующий конструктор будет выполнять простое копирование без выделения памяти из кучи для копируемого объекта, может возникнуть ситуация, когда два объекта будут считать, что именно они являются владельцами одного блока памяти. Ситуация еще более усугубится при вызове деструкторов обоих объектов, которые попытаются освободить одну и ту же память. Взгляните на приведенный ниже пример. linclude <lostream.h> iinclude <string.h> class Person public: Person(char *pN) cout Создаем pN \n ; oName = new char[strlen(pN) + 1]; if (pName != 0) strcpy (pNam.e, pN) ; -Person 0 { cout << Ликвидируем << pName << \n ; в качестве действия деструктора сотрем имя pName[О] = \ О ; delete pName; protected: char *pName; int main(t argcs, * pArgs i ]. Person pi ( ); Person p2 = pi; return 0; Вызов копирующего конструктора В этом примере конструктор для Person в]делит память из кучи для хранения в ней имени произвольной длины, что невозможно при использовании массивов. Деструктор возвращает эту память в кучу. Основная программа создает объект р1, описывающий человека, после чего создается копия этого объекта - р2. После запуска этой программы вы получите сообщение только от одного конструктора. Это неудивительно, поскольку копия р2 создается с помощью предоставляемого C++ конструктора копирования по умолчанию, а он не выводит никаких сообщений. Однако, после того как р1 и р2 выходят из области видимости, вы не получите двух сообщений о ликвидации объектов, как можно было ожидать. Более того, если вы выполните эту программу в пошаговом режиме в каком-либо отладчике, то получите сообщение об ошибке. Что же происходит? Конструктор вызывается один раз и выделяет блок памяти из кучи для хранения в нем имени человека. Копирующий конструктор, создаваемый C+ + , просто копирует этот адрес в новый объект, без выделения нового блока памяти. Когда объекты ликвидируются, деструктор для р2 первым получает доступ к этому блоку памяти. Этот деструктор стирает имя и освобождает блок памяти. К тому времени как деструктор получает доступ к этому блоку, память уже очищена, а имя стерто. Теперь понятно, откуда взялось сообщение об ошибке. Суть проблемы проиллюстрирована на рис. 19.1. Объект р1 копируется в новый объект р2, но не копируются используемые им ресурсы. Таким образом, и р2 указывают на один и тот же ресурс (в данном случае это блок памяти). Такое явление называется мелким (shallow) копированием, поскольку при этом копируются только члены класса как таковые. Рис I. Мелкое копирование объекту! вр2 Для решения указанной проблемы нужен такой копирующий конструктор, кото-р1й будет выделять ресурсы для нового объекта. Давайте добавим такой конструктор к классу и посмотрим, как он работает. class Person public: копирующий конструктор выделяет новый блек памяти из кучи Perscn {Persons: р) cout << Копируем <.<p.pName в отдельный блок\п ; fName = new char[strlen(p.pName) if (pName != 0) strcpy<pName, p.pNaine); остальное оставляем без Здесь копирующий конструктор в]деляет новый блок памяти для имени, а затем копирует содержимое блока памяти исходного объекта в этот новый блок (рис. 19,2). Такое копирование называется глубоким (deep), поскольку копирует не только элементы, но и занятые ими ресурсы (конечно, аналогия, как говорится, притянута за уши, но ничего не поделаешь - не я придумал эти термины). Рис. 19.2. Глубокое копирование!вр2 Запуск программы с новым копирующим конструктором приведет к выводу на экран следующих строк: Создаем Randy Копируем Randy в отдельный блок Ликвидируем Randy Ликвидируем Randy
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |