Выбрать главу

Чтобы глубже понять назначение конструкторов копии, рассмотрим подробнее их роль в каждой из этих трех ситуаций.

Конструкторы копии и параметры функции

При передаче объекта функции в качестве аргумента создается копия этого объекта. Если в классе определен конструктор копии, то именно он и вызывается для создания копии. Рассмотрим программу, в которой используется конструктор копии для надлежащей обработки объектов типа myclass при их передаче функции в качестве аргументов. (Ниже приводится корректная версия некорректной программы, представленной выше в этой главе.)

// Использование конструктора копии для

// определения параметра.

#include <iostream>

#include <cstdlib>

using namespace std;

class myclass {

  int *p;

 public:

  myclass(int i); // обычный конструктор

  myclass(const myclass &ob); // конструктор копии

  ~myclass();

  int getval() { return *p; }

};

// Конструктор копии.

myclass::myclass(const myclass &obj)

{

 p = new int;

 *p = *obj.p; // значение копии

 cout << "Вызван конструктор копии.\n";

}

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

myclass::myclass(int i)

{

 cout << "Выделение памяти, адресуемой указателем p.\n";

 р = new int;

 *p = i;

}

myclass::~myclass()

{

 cout <<"Освобождение памяти, адресуемой указателем p.\n";

 delete p;

}

// Эта функция принимает один объект-параметр.

void display(myclass ob)

{

 cout << ob.getval() << '\n';

}

int main()

{

 myclass a(10);

 display(a);

 return 0;

}

Эта программа генерирует такие результаты.

Выделение памяти, адресуемой указателем р.

Вызван конструктор копии.

10

Освобождение памяти, адресуемой указателем р.

Освобождение памяти, адресуемой указателем р.

При выполнении этой программы здесь происходит следующее: когда в функции main() создается объект а, "стараниями" обычного конструктора выделяется память, и адрес этой области памяти присваивается указателю а.р. Затем объект а передается функции display(), а именно— ее параметру ob. В этом случае вызывается конструктор копии, который создает копию объекта а. Конструктор копии выделяет память для этой копии, а значение указателя на выделенную область памяти присваивает члену р объекта-копии. Затем значение, адресуемое указателем р исходного объекта, записывается в область памяти, адрес которой хранится в указателе р объекта-копии. Таким образом, области памяти, адресуемые указателями а.р и ob.р, раздельны и независимы одна от другой, но хранимые в них значения (на которые указывают а.р и ob.р) одинаковы. Если бы конструктор копии не был определен, то в результате создания по умолчанию побитовой копии члены а.р и ob.р указывали бы на одну и ту же область памяти.

По завершении функции display() объект ob выходит из области видимости. Этот выход сопровождается вызовом его деструктора, который освобождает область памяти, адресуемую указателем ob.р. Наконец, по завершении функции main() выходит из области видимости объект а, что также сопровождается вызовом его деструктора и соответствующим освобождением области памяти, адресуемой указателем а.р. Как видите, использование конструктора копии устраняет деструктивные побочные эффекты, связанные с передачей объекта функции.

Использование конструкторов копии при инициализации объектов