Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП.docx
Скачиваний:
7
Добавлен:
01.07.2025
Размер:
68.18 Кб
Скачать

2.11 Конструктор копирования

В C++ методом передачи параметров по умолчанию является передача объектов по значению. При передаче объекта в функцию появляется новый объект. Когда работа функции, которой был передан объект, завершается, то удаляется копия аргумента. Как формируется копия объекта и вызывается ли деструктор объекта, когда удаляется его копия?

То, что вызывается деструктор копии, наверное, понятно, поскольку объект (копия объекта, передаваемого в качестве параметра) выходит из области видимости. Объект внутри функции - это побитовая копия передаваемого объекта, а это значит, что если объект содержит в себе, например, некоторый указатель на динамически выделенную область памяти, то при копировании создается объект, указывающий на ту же область памяти. И как только вызывается деструктор копии, где, как правило, принято высвобождать память, то высвобождается область памяти, на которую указывал объект-«оригинал», что приводит к разрушению исходного объекта.

Похожая проблема возникает и при использовании объекта в качестве возвращаемого значения. Для того чтобы функция могла возвращать объект, нужно: во-первых, объявить функцию так, чтобы ее возвращаемое значение имело тип класса, во-вторых, возвращать объект с помощью обычного оператора return. Однако если возвращаемый объект содержит деструктор, то в этом случае возникают похожие проблемы, связанные с « неожиданным » разрушением объекта.

Одним из способов обойти такого рода проблемы является создание особого типа конструкторов: конструкторов копирования. Конструктор копирования или конструктор копии позволяет точно определить порядок создания копии объекта.

Любой конструктор копирования имеет следующую форму.

имя_класса (const имя_класса & obj)

{

... // тело конструктора

}

Здесь obj - это ссылка на объект или адрес объекта. Конструктор копирования вызывается всякий раз, когда создается копия объекта. Мы уже рассмотрели два таких случая. Во-первых, при передаче объекта в качестве параметра функции. Во-вторых, при создании временного объекта тогда, когда функция в качестве возвращаемого значения использует объект. Есть еще один случай, когда полезен конструктор копирования, - это инициализация одного объекта другим.

class ClassName

{

public:

ClassName()

{

cout << 'Работа конструктора \n';

}

ClassName(const ClassName& obj)

{

cout << 'Работа конструктора копирования\n';

}

~ClassName()

{

cout << 'Работа деструктора \n';

}

};

main()

{

ClassName c1; // вызов конструктора

ClassName c2 = c1; // вызов конструктора копирования

}

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

2.12 Указатели и ссылки на объекты

Доступ к членам объекта можно осуществлять и через указатель на объект. В этом случае обычно применяется операция « ->». Указатель на объект объявляется точно так же, как и указатель на переменную любого типа. Для получения адреса объекта перед ним необходим оператор « &».

main()

{

_3d A (2,3,4);

_3d *pA;

pA = &A;

double dM = pA->mod();

}

В C++ есть элемент, родственный указателю, - это ссылка. Ссылка является скрытым указателем и всегда работает в качестве другого (дополнительного) имени переменной.

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

void ToZero(_3d *vec)

{

vec->set(0,0,0); // используется "->"

}

main()

{

_3d A(2,3,4);

ToZero(&A);

}

В C++ можно сделать то же самое без использования указателя с помощью параметра-ссылки.

void ToZero(_3d &vec)

{

vec.set(0,0,0); // используется "."

}

main()

{

_3d A(2,3,4);

ToZero(A);

}

При применении параметра-ссылки компилятор передает адрес переменной, используемой в качестве аргумента. При этом не только не нужно, но и неверно использовать оператор « *». Внутри функции компилятор использует переменную, на которую ссылается параметр-ссылка.

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

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

Ссылки могут также использоваться в качестве возвращаемого значения функции. Обычно такой механизм применяется в сочетании со ссылкой-параметром или указателем this. Все эти случаи объединяет необходимость передачи измененного объекта через его адрес, а не путем возвращения копии такого объекта. Вспомним хотя бы пример с переопределением оператора «=», где в качестве возвращаемого значения выступала копия измененного оператором «=» объекта. Теперь мы знаем , что можно сделать проще и эффективнее, если возвращать ссылку на измененный объект.

_3d& _3d::operator = (_3d& b)

{

x = b.x;

y = b.y;

z = b.z;

return *this;

}

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

3. Наследование на языке С++