Инструменты пользователя

Инструменты сайта


konstruktor_kopirovanija

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

konstruktor_kopirovanija [2010/12/05 19:43] (текущий)
Строка 1: Строка 1:
 +====== Конструктор копирования ======
 +<note important>​Возьмите за правило создавать конструктор копирования,​ если в классе описана хотя бы одна динамическая переменная.</​note>​
 +  * **Проблема.** Передача объекта в функцию.
 +Объекты класса можно передавать в функции в качестве аргументов точно так же, как передаются данные других типов. Однако,​ следует помнить,​ что в языках С и С++ методом передачи параметров,​ по умолчанию является передача объектов по значению. Это означает,​ что внутри функции создается копия объекта - аргумента,​ и эта копия, а не сам объект,​ используется функцией. Следовательно,​ изменения копии объекта внутри функции не влияют на сам объект.
  
 +Объект внутри функции - это побитовая копия передаваемого объекта,​ а это значит,​ что если объект содержит в себе, например,​ некоторый указатель на динамически выделенную область памяти,​ то при копировании создается объект,​ указывающий на ту же область памяти. И как только вызывается деструктор копии, где, как правило,​ принято высвобождать память,​ то высвобождается область памяти,​ на которую указывал объект-"​оригинал",​ что приводит к разрушению исходного объекта. ​
 +  * **Проблема.** Возврат объекта из функции.
 +Для того чтобы функция могла возвращать объект,​ нужно: во-первых,​ объявить функцию так, чтобы ее возвращаемое значение имело тип класса,​ во-вторых,​ возвращать объект с помощью обычного оператора return. Однако если возвращаемый объект содержит деструктор,​ то в этом случае возникают проблемы,​ связанные с "​неожиданным"​ разрушением объекта. ​
 +  * **Проблема.** Инициализация одного объекта другим при создании.
 +В программировании есть еще один случай побитового копирования - инициализация одного объекта другим при создании:​
 +<​file>​
 +# include <​iostream>​
 +using namespace std;
 +
 +class ClassName
 +{
 +public:
 + ClassName ()
 + {
 + cout << "​ClassName!!!\n";​
 + }
 + ~ClassName ()
 + {
 + cout << "​~ClassName!!!\n";​
 + }
 +};
 +
 +void main()
 +{
 + ClassName c1;
 +
 + // Вот он!!! Момент побитового копирования.
 + ClassName c2=c1;
 +}
 +</​file>​
 +Результат работы программы:​
 +<​file>​
 +ClassName!!!
 +~ClassName!!!
 +~ClassName!!!
 +</​file>​
 +Конструктор вызывается один раз: для с1. Для с2 конструктор не срабатывает. Однако деструктор срабатывает для обоих объектов. А, поскольку,​ с2 является точной копией с1, деструктор,​ высвобождающий динамически выделенную память,​ вызывается дважды для одного и того же фрагмента этой памяти. Это неминуемо приведет к ошибке.
 +====== Решение проблемы ======
 +Одним из способов обойти такого рода проблемы является создание особого типа конструкторов,​ - конструкторов копирования. Конструктор копирования или конструктор копии позволяет точно определить порядок создания копии объекта. Любой конструктор копирования имеет следующую форму: ​
 +<​file>​
 +имя_класса (const имя_класса & obj)
 +{
 + ... // тело конструктора
 +}
 +</​file>​
 +Здесь obj - это ссылка на объект или адрес объекта. Конструктор копирования вызывается всякий раз, когда создается копия объекта. Таким образом,​ в конструкторе копирования можно выделить "​свою"​ память под новый объект.
 +<​file>​
 +# include <​iostream>​
 +using namespace std;
 +class ClassName
 +{
 +public:
 + ClassName ()
 + {
 + cout << "​ClassName!!!\n";​
 + }
 + ClassName (ClassName&​obj){
 + cout << "Copy ClassName!!!\n";​
 + }
 + ~ClassName ()
 + {
 + cout << "​~ClassName!!!\n";​
 + }
 +};
 +void f(ClassName o){
 + cout<<"​Function f!!!\n";​
 +}
 +
 +ClassName r(){
 + ClassName o;
 + cout<<"​Function r!!!\n";​
 + return o;
 +}
 +void main()
 +{
 + // инициализация одного объекта другим
 + ClassName c1;
 + ClassName c2=c1;
 +
 + // передача объекта в функцию
 + ClassName a;
 + f(a);
 +
 + //​возврат объекта из функции
 + r();
 +
 +}
 +</​file>​
 +Результат работы программы:​
 +<​file>​
 +// создался объект с1
 +ClassName!!!
 +
 +// инициализация объекта с2 объектом с1
 +Copy ClassName!!!
 +
 +// создался объект а
 +ClassName!!!
 +
 +// передача а в функцию по значению
 +// создалась копия о
 +Copy ClassName!!!
 +
 +// отработала функция f
 +Function f!!!
 +
 +// уничтожилась копия o
 +~ClassName!!!
 +
 +// создался объект o
 +// внутри функции r
 +ClassName!!!
 +
 +// отработала функция r
 +Function r!!!
 +
 +// возврат из функции ​
 +// создалась копия объекта о
 +Copy ClassName!!!
 +
 +// уничтожился объект o
 +~ClassName!!!
 +
 +// уничтожилась его копия
 +~ClassName!!!
 +
 +// уничтожился объект а
 +~ClassName!!!
 +
 +// уничтожился объект с2
 +~ClassName!!!
 +
 +// уничтожился объект с1
 +~ClassName!!!
 +</​file>​
 +<​note>​Конструктор копирования не влияет на операцию присваивания вида A=B. Здесь также срабатывает побитовое копирование,​ однако эту проблему решают в С++ иначе.</​note>​
 +Теперь,​ когда есть конструктор копирования,​ можно смело передавать объекты в качестве параметров функций и возвращать объекты. При этом количество вызовов конструкторов будет совпадать с количеством вызовов деструкторов,​ а поскольку процесс образования копий теперь стал контролируемым,​ существенно снизилась вероятность неожиданного разрушения объекта. Обратите внимание,​ что критическая ошибка вероятна только тогда, когда в конструкторе происходит динамическое обращение к памяти с целью выделения,​ а в деструкторе данная память удаляется.
 +<​note>​Помимо создания конструктора копирования есть другой способ организации взаимодействия между функцией и программой,​ передающей объект. Этот способ - передача объекта по ссылке или по указателю.</​note>​
загрузка...
konstruktor_kopirovanija.txt · Последние изменения: 2010/12/05 19:43 (внешнее изменение)