Мои Конспекты
Главная | Обратная связь


Автомобили
Астрономия
Биология
География
Дом и сад
Другие языки
Другое
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Металлургия
Механика
Образование
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Туризм
Физика
Философия
Финансы
Химия
Черчение
Экология
Экономика
Электроника

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 ( ) ;}

Void main ( )

{ My a1 (10) ;// создается объект, переменная инициализируется 10

My a2 = a1;// с помощью конструктора копирования создаетсяобъект

// a2,со значением переменной 11, т.к. в конструкторе

// инкремент

Print ( a1) ;

/* значение переменной объектаa1копируется с инкрементом в

в переменную локального объекта objфункции (11)и это

значение выводится */

Print ( a2 ) ;

/* значение переменной объектаa2копируется с инкрементом в

в переменную локального объекта objфункции (12)и это

значение выводится */

a1. ~My( ); // вызовом деструктора объект память a1 освобождается

Print ( a1) ; //выведется мусор

Print ( a2); //выведется 12

}

Результат:

После вызова деструктора a1освобождается динамическая память ив данном (*p) – члене объекта a1содержитсятеперь мусор(операция delete в процессе возврата в систему возвращаемой памяти затирает эту память).

Объект a2остался неизменным. Так и должно быть.

 

Если мы удалим из определения класса конструктор копирования, результат работы программы следующий:

 

Уничтожение объекта a1 привело к уничтожению и его копии a2 , в которой находится теперь тот же мусор.

 

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

 

При отсутствии конструктора копирования программа будет использовать конструктор копирования по умолчанию следующего вида:

 

My ( My & obj ) { p = obj.p; }

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

Побайтовое копирование объекта дает копирование указателя, т.е. в объект a2 заносится тот же самый указатель, оба объекта указывают на один и тот же участок памяти.

 

Объект a1

 
 


Данное (*p)

Объект

P
a2

 

При освобождении памяти в a1 (или в a2 )стирается наше единственное данное, т.е. и второй объект разрушается.

 




Поиск по сайту:







©2015-2020 mykonspekts.ru Все права принадлежат авторам размещенных материалов.