Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛЕКЦИИ Программирование и основы алгоритмизации...doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
1.5 Mб
Скачать

Перегруженная операция присваивания

Создадим объекты A и B.

Оператор присваивания B=A скопирует в B статические данные объекта A.

Так как указателю mem2 в B присваивается значение указателя mem2 в A, то оба указателя будут ссылаться на один и тот же участок памяти. Но динамическую память, выделенную для B, после присваивания ссылки не будет.Предположим, оператор присваивания находится в функции:

void F()

{ dyn<int> A(2,3), B(4,5);

------------------------------

B=A;

}

По возвращении из функции F все объекты, созданные в функции F, уничтожаются вызовами деструктора, освобождающего динамическую память, на которую указывает mem2. Предположим, объект B уничтожается первым. Деструктор освобождает память, на которую указывает B.mem2 (на неё же указывает и A.mem2). При уничтожении A вызывается его деструктор для освобождения памяти по указателю A.mem2. Но этот блок памяти уже освобожден при уничтожении B, поэтому использование операции delete для деструктора теперь является ошибкой.

Правильное присваивание B=A должно выполняться так (действия 1 – копировать, 2 - копировать):

Следовательно, для правильного присваивания нужно перегрузить операцию присваивания (=) включением соответствующей функции-элемента в класс.

//----------------------------------------------------------------------------------------------

template <class T>

dyn<T>& dyn<T>::operator=(const dyn<T>& r)

{ mem1=r.mem1; // 1 - копирование статического элемента объекта r

//в статический элемент текущего объекта

*mem2=*r.mem2; // 2 - копирование динамического элемента объекта r

//в динамический элемент текущего объекта

cout<<”Оператор присваивания: “<<mem1<<’|’<<*mem2<<endl;

return *this;

}

//----------------------------------------------------------------------------------------------

Благодаря тому, что параметр r передается по константной ссылке, в этот параметр копируется только адрес объекта и исключаются любые изменения объекта.

Поскольку операция (=) возвращает ссылку на текущий объект, то можно связывать операторы присваивания: C=B=A;

Конструктор 2 (конструктор копирования, конструктор копии)

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

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

1)dyn<int> A(3,5), B=A; //инициализация создаваемого объекта B

//данными имеющегося объекта A

2)Инициализация создаваемого локального объекта данными существующего объекта имеет место при передаче объекта в функции в качестве параметра по значению, и при возвращении копии объекта из функции в качестве возвращаемого значения.

dyn<int> f(dyn<int> x)

{ dyn<int> obj;

--------------

return obj;

}

-----------------------

dyn<int> A(3,5), B(0,0); //объявления объектов

-------------------

B=f(A); //вызов f копированием A в x, x – создаваемый локальный объект

При выполнении возврата из f(A) создается копия obj, вызываются деструкторы для локальных объектов x и obj, а копия obj возвращается как значение функции.

Схема выполнения конструктора копии:

1 – копировать;

2 – выделить память под *mem2;

3 – копировать.

//----------------------------------------------------------------------------------------------

template <class T>

dyn<T>::dyn(const dyn<T>& x)

{ mem1=x.mem1;

mem2=new T(*x.mem2);

cout <<”Конструктор копии: ”<<mem1<<’|’<<*mem2<<endl;

}

//----------------------------------------------------------------------------------------------

Примечания.

1) Конструктор копии используется только тогда, когда создается объект.

2) Параметр в конструкторе копии должен передаваться по ссылке.

Предположим, в конструкторе копии параметр передается по значению:

dyn(dyn<T> x),

т.е. объект A в конструкторе копии передается параметру x по значению. Это означает, что создается локальный объект x – копия A. Для этого вызывается конструктор копии. Этот вызов, в свою очередь, требует вызова конструктора копии, и получается бесконечная цепь вызовов конструктора копии.

Вызовы конструкторов и деструкторов выполняются автоматически. В общем случае вызовы конструкторов выполняются в порядке, обратном вызовам конструкторов. Однако локальные статические объекты разрушаются по выходу из основной функции, в отличие от локальных автоматических, которые разрушаются по выходу из вызываемой функции.