T sum ( T obj1 , T obj2 )

{ obj1. x + =obj2. x;

obj1. y + =obj2. y;

return obj1; }

#include <iostream.h>

void main () {

T e1 (1, 10); //создали объект, вызвав конструктор с параметрами

Print (e1); // первое копирование

T e2 = e1; // второе копирование

Print (e2); // третье копирование

T e3 = sum (e1, e2); // четвертое копирования

Print (e3); // пятое копирование

}

// выведется:

// x= 1 y = 10

x= 1 y = 10

x= 2 y = 20

Определение конструктора копирования

Конструктор копирования должен иметь для данного класса такой вид:

1. T (T obj) { x = obj.x; y = obj. y } // не верно!

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

Рекурсию можно избежать, если не копировать объект в конструктор, а использовать передачу параметра по ссылке:

2. T (T & obj) { x = obj.x; y = obj. y }

И последнее – необходимо ввести запрет на модификацию копируемого параметра для этого используется ключевое слово const:

3. T (const T & obj) { x = obj.x; y = obj. y }

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

Рассмотрим, как происходит копирование.

Как удостоверится, что

- при копировании объекта,

- при передачи объекта в функцию,

-при возвращении функцией объекта в некоторую

переменную (также объект)

вызывается конструктор копирования по умолчанию, или конструктор копирования явно определенный в классе?

Для того, чтобы зафиксировать факт вызова конструктора копирования, мы правильное копирование (в программе написанной выше, конструктор варианта 3 вызывается по умолчанию) заменим на копирование с приращением:

class T {

int x, y;

public:

T (int tx, int ty) { x = tx; y = ty; }

T (const T & obj) { x = obj.x+1; y = obj. y +1 }

int GetX () { return x; }

int GetY () { return y; }

friend T sum (T, T);

};

void Print (T obj)

{ cout<<’\n’<<”x=”<<obj.GetX() <<” y=”<<obj.GetY(); }

T sum (T obj1, T obj2) // хотя закрытые данные функция -друг

{ obj1. x + =obj2. x; // имеет право обращаться к компонентам

obj1. y + =obj2. y;

return obj1; }

#include <iostream.h>

void main () {

T e1 (1, 10); // создаем объект

Print (e1); // первое копирование - приращение

T e2 = e1; // второе копирование

Print (e2); // третье копирование

T e3 = sum (e1, e2); // четвертые копирования

Print (e3); // пятое копирование

}

// выведется:

// x= 2 y = 11

x= 3 y = 12

x= 7 y = 25

1) При копировании e1 в локальную переменную obj в функции Print,

в данные объекта obj передадутся значения x и y объекта e1 с

инкрементом, которые и будут выведены функцией (x = 2 y = 11)

2) При копировании в e1 в e2, в объект e2 передадутся

инкрементированные значения x и y объекта e1 (2 и 11- значения

данных объекта e2).

3) Затем при вызове Print(e2), значения объекта e2 копируются в локальную

переменную obj в функции Print. В obj передадутся значения x и y из

e2 с инкрементом они и будут выведены (x= 3 y = 12).

4) Затем в локальные переменные obj1 obj2 функции sum копируются

данные объектов e1 e2 с инкрементом в obj1 - (2 11), в obj2 - (3 12).

Локальные объекты суммируются в функции. Результирующий объект

имеет значения данных (5 23).

Данные этого объекта копируются с инкрементом в данные объекта e3 в

выражении T e3 = sum (…); т.е. e3 получает данные (6 24).

5) При вызове Print(e3), значения объекта e3 копируются в локальную

переменную obj в функции Print. В obj передадутся значения x и y из

e3 с инкрементом они и будут выведены (x= 7 y = 25).

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

Обязательное его определение в тех случаях, когда класс содержит поля, являющиеся указателями на динамические участки памяти.

#include <iostream.h>

#include <conio.h>

class My {

int* p;

public:

My (int a) // встроенный конструктор с параметром

{ p= new int; *p = a; }

My (const My & obj) // конструктор копирования с инкрементом

{ p = new int; *p = *obj.p+1; }

/* Для фиксации вызова конструктора копирование также проводится с

инкрементом.

Конструктор копирования, создавая копию объекта, сначала выделяет

память под данное- член нового объекта и затем присваивает ему

значение, взятое из объекта – аргумента. Правильное присваивание

(без инкремента): *p = *obj.p;

*/

int Get () { return * p } // возвращает значение по адресу указателя

~My () { delete (p); } // деструктор память, выделенную под данное

// объекта освобождает

};

void Print (My obj) // функция вывода на экран самой переменной

{ cout<< ‘\n’ << obj.Get ();}


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: