Конструктор копирования C++
- Проблема. Передача объекта в функцию.
Объекты класса можно передавать в функции в качестве аргументов точно так же, как передаются данные других типов. Однако, следует помнить, что в языках С и С++ методом передачи параметров, по умолчанию является передача объектов по значению. Это означает, что внутри функции создается копия объекта - аргумента, и эта копия, а не сам объект, используется функцией. Следовательно, изменения копии объекта внутри функции не влияют на сам объект.
Объект внутри функции - это побитовая копия передаваемого объекта, а это значит, что если объект содержит в себе, например, некоторый указатель на динамически выделенную область памяти, то при копировании создается объект, указывающий на ту же область памяти. И как только вызывается деструктор копии, где, как правило, принято высвобождать память, то высвобождается область памяти, на которую указывал объект-"оригинал", что приводит к разрушению исходного объекта.
- Проблема. Возврат объекта из функции.
Для того чтобы функция могла возвращать объект, нужно: во-первых, объявить функцию так, чтобы ее возвращаемое значение имело тип класса, во-вторых, возвращать объект с помощью обычного оператора return. Однако если возвращаемый объект содержит деструктор, то в этом случае возникают проблемы, связанные с "неожиданным" разрушением объекта.
- Проблема. Инициализация одного объекта другим при создании.
В программировании есть еще один случай побитового копирования - инициализация одного объекта другим при создании:
# include <iostream> using namespace std; class ClassName { public: ClassName () { cout << "ClassName!!!\n"; } ~ClassName () { cout << "~ClassName!!!\n"; } }; void main() { ClassName c1; // Вот он!!! Момент побитового копирования. ClassName c2=c1; }
Результат работы программы:
ClassName!!! ~ClassName!!! ~ClassName!!!
Конструктор вызывается один раз: для с1. Для с2 конструктор не срабатывает. Однако деструктор срабатывает для обоих объектов. А, поскольку, с2 является точной копией с1, деструктор, высвобождающий динамически выделенную память, вызывается дважды для одного и того же фрагмента этой памяти. Это неминуемо приведет к ошибке.
Решение проблемы
Одним из способов обойти такого рода проблемы является создание особого типа конструкторов, - конструкторов копирования. Конструктор копирования или конструктор копии позволяет точно определить порядок создания копии объекта. Любой конструктор копирования имеет следующую форму:
имя_класса (const имя_класса & obj) { ... // тело конструктора }
Здесь obj - это ссылка на объект или адрес объекта. Конструктор копирования вызывается всякий раз, когда создается копия объекта. Таким образом, в конструкторе копирования можно выделить "свою" память под новый объект.
# 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(); }
Результат работы программы:
// создался объект с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!!!
Теперь, когда есть конструктор копирования, можно смело передавать объекты в качестве параметров функций и возвращать объекты. При этом количество вызовов конструкторов будет совпадать с количеством вызовов деструкторов, а поскольку процесс образования копий теперь стал контролируемым, существенно снизилась вероятность неожиданного разрушения объекта. Обратите внимание, что критическая ошибка вероятна только тогда, когда в конструкторе происходит динамическое обращение к памяти с целью выделения, а в деструкторе данная память удаляется.
📌 Для тестирования скриптов, установщиков VPN, Python ботов рекомендуем использовать надежные VPS на короткий срок. Если вам нужна помощь с более сложными задачами, вы можете найти фрилансера, который поможет с настройкой. Узнайте больше о быстрой аренде VPS для экспериментов и о фриланс-бирже для настройки VPS, WordPress. 📌
💥 Подпишись в Телеграм 💥 и задай вопрос по сайтам и хостингам бесплатно!7 Самых Популярных Статей
- Как запустить скрипты и веб-приложения на Python
- Что такое страны TIER 1,2,3
- 7 способов сравнения файлов по содержимому в Windows или Linux
- Установка и тестирование веб-панели HestiaCP
- Китайский VPN Shadowsocks простая установка и настройка
- top, htop, atop определение загрузки ОС (Load average, LA)
- Использование rsync в примерах