Конструктор копирования в С++

Думаю, главный вопрос, при изучении любой темы по программированию – Зачем?  И на него надо отвечать в первую очередь, а потом уже показывать, как и когда надо применять  те или иные инструменты программирования.

Конструктор копирования нужен нам для того, чтобы создавать «реальные» копии объектов класса, а не побитовую копию объекта. Иногда это принципиально важно. Такую «реальную» копию объекта надо создавать в нескольких случаях:

  • когда мы передаем объект в какую-либо функцию в виде параметра;
  • когда какая-либо функция должна вернуть объект класса в результате своей работы;
  • когда мы в главной функции один объект класса инициализируем другим объектом класса.

Например, мы передаем объект в функцию в виде параметра. Функция будет работать не с самим переданным объектом, а с его побитовой копией. Допустим в конструкторе класса, при создании объекта, выделяется определенный объем памяти, а деструктор класса эту память освобождает. Указатель побитовой копии объекта будет хранить тот же адрес памяти, что и оригинальный объект. И, когда при завершении работы функции и уничтожении побитовой  копии объекта, сработает деструктор, он обязательно освободит память,  которая была занята объектом-оригиналом. В придачу, еще и при завершении работы программы, деструктор сработает повторно и попытается еще раз освободить этот объем памяти, что неизбежно приведет к ошибкам программы. Та же участь постигнет и память, выделенную для указателя объекта, если будет удаляться побитовая копия возвращаемого функцией объекта, и побитовая копия при инициализации объекта класса другим объектом.

Чтобы избежать этих проблем и ошибок существует конструктор копирования. Его работа заключается в том, чтобы создать реальную копию объекта со своей личной выделенной динамической памятью. Синтаксис конструктора копирования следующий:

имяКласса (const имяКласса & object)
{
    //код конструктора копирования
}

Рассмотрим простой пример. В нем создадим класс, который будет содержать обычный конструктор, конструктор копирования и деструктор. В этом примере будут описаны все три случая для которых надо применять конструктор копирования.  Для того, чтобы пример не был слишком большим, мы не будем заставлять конструкторы выделять память, а деструктор освобождать память. Пропишем в них только вывод определенного сообщения на экран. Таким образом, можно будет посмотреть, сколько раз сработают конструкторы и деструктор. Как вы понимаете, если бы деструктор освобождал память, то количество его вызовов не должно превышать количество вызовов конструкторов.

Пример:

#include <iostream>
using namespace std;

class SomeClass
{	
	int *ptr; // указатель на какой-либо участок памяти
public:	

	SomeClass() //  конструктор
	{
		cout << "\nОбычный конструктор\n";
	}
	SomeClass(const SomeClass &obj)
	{		
		cout << "\nКонструктор копирования\n";
	}
	~SomeClass()	
	{
		cout << "\nДестркуктор\n";
	}
};
	void funcShow(SomeClass object) 
	{
		cout << "\nФункция принимает объект, как параметр\n";
	}

	SomeClass funcReturnObject()
	{	
		SomeClass object;		
		cout <<	"\nФункция возвращает объект\n";
		return object;
	}

int main()
{
	setlocale(LC_ALL,"rus");

	cout << "1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
	SomeClass obj1; // создаем объект класса

	cout << "2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
	funcShow(obj1); // передаем объект в функцию

	cout << "3 - 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
	funcReturnObject(); // эта функция возвращает объект

	cout << "5 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";
	SomeClass obj2 = obj1;	// инициализация объекта при создании

}

В строках 4 — 21 определен класс. Обычный конструктор, строки 9 — 12, будет автоматически вызваться при создании новых объектов класса. А конструктор копирования, строки 13 — 16, будет автоматически вызваться при создании копии объекта. Деструктор, строки 17 — 20, вызывается всякий раз когда удаляется либо объект, либо его копия. Ниже определены две функции funcShow() и funcReturnObject(). Первая принимает объект в виде параметра, вторая при вызове во-первых создает новый объект (тут сработает обычный конструктор), а во-вторых возвращает объект (тут сработает конструктор копирования т.к. при возврате объекта из функции создается его временная копия).

Вот какую картинку мы увидим при запуске программы:

CppStudio.com

1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Обычный конструктор
2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Конструктор копирования

Функция принимает объект, как параметр

Дестркуктор
3 — 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Обычный конструктор

Функция возвращает объект

Конструктор копирования

Дестркуктор

Дестркуктор
5 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Конструктор копирования

Дестркуктор

Дестркуктор
Для продолжения нажмите любую клавишу . . .

Пойдем по порядку. В первом блоке сработал обычный конструктор — это произошло при создании объекта класса obj1. Второй блок отображает работу функции funcShow(). Так как при передаче объекта в функцию создается его копия и сработал конструктор копирования. А при завершении работы функции эта копия уничтожается и срабатывает деструктор. Блок 3 — 4 показывает, что при создании нового объекта в функции funcReturnObject() сработал обычный конструктор, а при возврате объекта — конструктор копирования. Деструктор, как положено отработал дважды — для оригинала объекта и для копии. И наконец в пятом блоке срабатывает конструктор копирования при инициализации нового объекта класса. Затем дважды деструктор  — один уничтожает копию объекта пятого блока, второй уничтожает объект из первого блока. Ну как-то так. В итоге количество вызовов деструктора совпадает с вызовами конструкторов.

А теперь попробуйте закомментировать конструктор копирования в определении класса и запустите программу.  Вы увидите, что конструктор сработает только дважды, а деструктор, рад стараться, отработает пять раз. И если бы он освобождал память — это неизбежно  привело бы к ошибке программы.

Практика

К сожалению, для данной темы пока нет подходящих задач. Если у вас есть таковые на примете, отправте их по адресу: admin@cppstudio.com. Мы их опубликуем!

Автор: Marienko L.
Дата: 19.03.2014
Поделиться:

Комментарии

  1. Maks Maksuta

    Статья маленька но очень понятная. Целый час копаюсь в гугле, пытаясь найти что-то адекватное про конструктор окпирования. Это первый сайт где все коротко и понятно!

    Спасибо.

  2. blablabla

    ничего непонятно

Оставить комментарий

Вы должны войти, чтобы оставить комментарий.