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

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


konstruktor_kopirovanija

Различия

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

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

konstruktor_kopirovanija [2019/05/12 13:35]
konstruktor_kopirovanija [2020/06/13 13:46] (текущий)
Строка 1: Строка 1:
 +====== Конструктор копирования C++ ======
 +<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>