ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ В C++ (4-Е ИЗДАНИЕ) (часть 11) онлайн
Конструктор копирования
Мы уже обсуждали, что можно определять объекты и одновременно их инициа-
лизировать другими с помощью двух видов выражений:
alpha аЗ(а2); //инициализация копирования
alpha аЗ = а2; //инициализация копирования.
// альтернативная запись
Оба стиля определения включают в себя конструктор копирования, то есть
конструктор, создающий новый объект и копирующий в него свои аргументы.
По умолчанию этот конструктор производит поэлементное копирование. Это
очень похоже на то, что делает оператор присваивания, с той лишь разницей,
что конструктор создает новый объект.
Как и оператор присваивания, конструктор копирования может быть пере-
гружен. В программе XOFXREF мы покажем, как это делается.
Листинг 11.17. Программа XOFXREF
// xofxref.cpp
// конструктор копирования: X(X&)
#include <iostream>
using namespace std;
///////////////////////////////////////////////////////////
class alpha
{
private:
int data;
public:
alpha() //конструктор без аргументов
{ }
alpha(int d) //конструктор с одним аргументом
{ data = d; }
alpha(alpha& a) //конструктор копирования
{
data = a.data;
cout << "\nЗапущен конструктор копирования";
}
void display() //display
{ cout << data; }
void operator = (alpha& a) //overloaded = operator
{
data = a.data;
cout << "\nЗапущен оператор присваивания";
}
};
///////////////////////////////////////////////////////////
int main()
{
alpha a1(37);
alpha a2;
a2 = a1; //запуск перегружаемого =
cout << "\na2="; a2.display(); //вывести a2
alpha a3(a1); //запуск конструктора копирования
// alpha a3 = a1; //эквивалентное определение a3
cout << "\na3="; a3.display(); //вывести a3
cout << endl;
return 0;
}
В этой программе перегружается и оператор присваивания, и конструктор
копирования. Перегружаемое присваивание подобно аналогичному из програм-
мы ASSIGN. У конструктора копирования имеется один аргумент — объект типа
alpha, переданный по ссылке. Вот его описатель:
alpha(alpha& a)
Он имеет вид Х(Х&). Ниже приведен результат работы программы:
Запущен оператор присваивания
а2=37
Запущен конструктор копирования
а3=37
Выражение
а2 = a1;
запускает оператор присваивания, тогда как
alpha a3(a1);
запускает конструктор копирования. Для последней цели может быть использо-
вано другое эквивалентное выражение:
alpha аЗ = a1;
Как уже было показано, конструктор копирования может быть запущен во
время определения объекта. Он также запускается, когда аргументы передаются
функциям по значению и когда возвращается результат работы функций. Давай-
те кратко рассмотрим эти ситуации.
Аргументы функции
Итак, конструктор копирования запускается при передаче по значению аргумен-
та функции. Он создает копию объекта, с которой функция и работает. Таким
образом, если бы функция
void func(alpha);
была объявлена в XOFXREF и если бы она вызывалась выражением
func(a1);
то конструктор копирования создал бы копию объекта a1 для его использова-
ния func(). Само собой разумеется, что конструктор копирования не запускается
при передаче аргумента по ссылке или при передаче указателя на объект. В этих
случаях никаких копий не создается, и функция работает с исходной перемен-
ной.
Возврат результатов работы функции
Конструктор копирования также создает временный объект, когда значение воз-
вращается из функции в основную программу. Допустим, что в XOFXREF была ка-
кая-либо подобная функция:
alpha func();
и она вызывалась выражением
a2 = func();
Конструктор копирования в этом случае запустился бы для того, чтобы соз-
дать копию возвращаемого функцией func() результата, а затем, после запуска
оператора присваивания, этот результат был бы сохранен в а2.
Почему не просто x(x)?
А надо ли нам использовать ссылку в аргументе конструктора копирования?
Может быть, можно просто передать значение? Но нет, компилятор сообщает о
«выходе за пределы памяти» при попытке откомпилировать выражение
alpha(alpha а)
Почему? Да потому, что при передаче аргумента по значению создается его
копия. Кто ее создает? Конструктор копирования. Но мы ведь и запускаем кон-
структор копирования, то есть он пытается запустить самого себя, поймать соб-
ственный хвост, до тех пор, пока не выйдет за пределы памяти. Так что в конст-
руктор копирования аргумент должен передаваться по ссылке, что не приведет
к созданию копий объекта.
Следите за деструкторами
В разделах «Передача по ссылке» и «Возврат значений» мы обсуждали передачу
функциям аргументов по значению и возврат результатов в вызывающую про-
грамму по значению. В этих ситуациях также запускается деструктор, когда при
выходе из функции уничтожаются временные объекты. Это может вызвать недо-
умение, если вы не ожидаете такого поворота событий. Мораль сей басни такова:
работая с объектами, которым требуется нечто большее, нежели простое поэле-
ментное копирование, передавайте и возвращайте данные по ссылкам везде, где
это возможно.
Определение и конструктора копирования,
и оператора присваивания
Перегружая оператор присваивания, вы почти наверняка намереваетесь заодно
переопределить и конструктор копирования (и наоборот). Дело в том, что вам не
всегда требуется использовать эти перегружаемые операции, но, с другой сторо-
ны, есть места в программах, где они необходимы. Даже если вы не предполага-
ете использовать в программе одно либо другое, можно заметить, что компилятор
сам использует различные варианты поведения в сложных ситуациях, таких, как,
например, передача аргумента по значению или возврат результата по значению.
Фактически, если конструктор класса включает в себя операции по работе с
системными ресурсами — памятью, файлами на диске, — вам следует почти все-
гда перегружать и конструктор копирования, и оператор присваивания, не забы-
вая убедиться в том, что они работают корректно.
Запрещение копирования
Мы обсудили, как настроить процедуру копирования объектов с помощью кон-
структора копирования и оператора присваивания. Но иногда бывает нужно за-
претить копирование. Сделать это можно, используя те же инструменты. Напри-
мер, может быть важно то, что каждый член класса создается с уникальным
значением определенного элемента данных, который поставляется в качестве ар-
гумента конструктору. Но вы ведь понимаете, что если создастся копия объекта,
ей будет присвоено то же значение, и объект перестанет быть уникальным. Что-
бы избежать копирования, просто-напросто перегружайте присваивание и кон-
структор копирования, делайте их скрытыми членами класса:
class alpha
{
private:
alpha& operator = (alpha&); //Скрытое присваивание
alpha(alpha&); //Скрытое копирование
};
Как только вы попытаетесь скопировать информацию
alpha a1,а2;
a1 = а2; //присваивание
alpha a3(a1); //копирование
компилятор сообщит, что функция недоступна. Вам нет никакой необходимости
определять функции, которые никогда не будут вызваны.
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
Схожі підручники
- Українська мова за професійним спрямуванням. Навчальний посібник (частина 4)
- Методичні вказівки до виконання практичного заняття на тему «Післяоптимізаційний аналіз розв’язку економічних задач »
- Лекції з Філософії (частина 1)
- Прежде чем начать СВОЙ БИЗНЕС (онлайн)
- Загальні питання з курсу Вища Математика
- Основы эффективных продаж (онлайн)
