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() выходит из области видимости объект а, что также сопровождается вызовом его деструктора и соответствующим освобождением области памяти, адресуемой указателем а.р. Как видите, использование конструктора копии устраняет деструктивные побочные эффекты, связанные с передачей объекта функции.

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

Конструктор копии также вызывается в случае, когда один объект используется для инициализации другого.

Рассмотрим следующую простую программу.

// Вызов конструктора копии для инициализации объекта.

#include

#include

using namespace std;

class myclass {

  int *p;

 public:

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

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

  ~myclass();

  int getval() { return *p; }

};

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

myclass::myclass(const myclass &ob)

{

 p = new int;

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

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

}

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

myclass::myclass(int i)

{

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

 р = new int;

 *р = i;

}

myclass::~myclass()

{

 cout << "Освобождение р-памяти.\n";

 delete p;

}

int main()

{

 myclass a(10); // Вызывается обычный конструктор.

 myclass b = a; // Вызывается конструктор копии.

 return 0;

}

Результаты выполнения этой программы таковы.

Выделение p-памяти обычным конструктором.

Выделение p-памяти конструктором копии.

Освобождение р-памяти.

Перейти на страницу:

Все книги серии Изучайте C++ с профессионалами

Похожие книги