Категории
Самые читаемые
Лучшие книги » Компьютеры и Интернет » Программирование » Язык программирования C++. Пятое издание - Стенли Липпман

Язык программирования C++. Пятое издание - Стенли Липпман

Читать онлайн Язык программирования C++. Пятое издание - Стенли Липпман

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 165 166 167 168 169 170 171 172 173 ... 297
Перейти на страницу:

• передаче объекта как аргумента параметру не ссылочного типа;

• возвращении объекта из функции с не ссылочным типом возвращаемого значения;

• инициализации списком в скобках элементов массива или членов агрегатного класса (см. раздел 7.5.5)

Некоторые классы используют также инициализацию копией для резервируемых объектов. Например, библиотечные контейнеры инициализируют копией свои элементы при инициализации контейнера, либо при вызове функции insert() или функции push() элемента (см. раздел 9.3.1). Элементы, созданные функцией emplace(), напротив, отличаются прямой инициализацией (см. раздел 9.3.1).

Параметры и возвращаемые значения

Во время вызова функции с параметрами не ссылочного типа осуществляется инициализация копией (см. раздел 6.2.1). Точно так же, когда у функции не ссылочный тип возвращаемого значения, возвращаемое значение используется в точке вызова для инициализации копией результата оператора вызова (см. раздел 6.3.2).

Тот факт, что конструктор копий используется для инициализации не ссылочных параметров типа класса, объясняет, почему собственный параметр конструктора копий должен быть ссылкой. Если бы этот параметр не был ссылкой, то вызов не был бы успешным — при вызове конструктора копий должен быть использован конструктор копий для копирования аргумента, но для копирования аргумента следует вызвать конструктор копий и так далее до бесконечности.

Ограничения на инициализацию копией

Как уже упоминалось, используется ли инициализация копией или прямая инициализация, если используется инициализатор, то потребуется преобразование в явный конструктор (см. раздел 7.5.4):

vector<int> v1(10);  // ok: прямая инициализация

vector<int> v2 = 10; // ошибка: конструктор, получающий размер,

                     // является явным

void f(vector<int>); // параметр f() инициализируется копией

f(10); // ошибка: нельзя использовать явный конструктор для

       // копирования аргумента

f(vector<int>(10));  // ok: непосредственно создать временный вектор

                     // из int

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

Компилятор может обойти конструктор копий

Во время инициализации копией компилятору можно (но не обязательно) пропустить конструктор копий или перемещения и создать объект непосредственно. Таким образом, код

string null_book = "9-999-99999-9"; // инициализация копией

компилятор может выполнить так:

string null_book("9-999-99999-9"); // компилятор пропускает конструктор

                                   // копий

Но даже если компилятор обойдет вызов конструктора копий или перемещения, то он все равно должен существовать и быть доступен (не должен быть закрытым, например) в этой точке программы.

Упражнения раздела 13.1.1

Упражнение 13.1. Что такое конструктор копий? Когда он используется?

Упражнение 13.2. Объясните, почему следующее объявление недопустимо:

Sales_data::Sales_data(Sales_data rhs);

Упражнение 13.3. Объясните, что происходит при копировании объектов классов StrBlob и StrBlobPtr?

Упражнение 13.4. Предположим, класс Point имеет открытый конструктор копий. Укажите каждый случай использования конструктора копий в этом фрагменте кода:

Point global;

Point foo_bar(Point arg) {

 Point local = arg, *heap = new Point(global);

 *heap = local;

 Point pa[4] = { local, *heap };

 return *heap;

}

Упражнение 13.5. Напишите с учетом следующего эскиза класса конструктор копий, копирующий все переменные-члены. Конструктор должен динамически резервировать новую строку (см. раздел 12.1.2) и копировать объект, на который указывает ps, а не сам указатель ps.

class HasPtr {

public:

 HasPtr(const std::string &s = std::string()) :

  ps(new std::string(s)), i(0) { }

private:

 std::string *ps;

 int i;

};

13.1.2. Оператор присвоения копии

Подобно тому, как класс контролирует инициализацию своих объектов, он контролирует также присваивание своих объектов:

Sales_data trans, accum;

trans = accum; // использует оператор присвоения копии

               // класса Sales_data

Компилятор сам синтезирует оператор присвоения копии, если он не определен в классе явно.

Перегруженный оператор присвоения

Прежде чем перейти к синтезируемому оператору присвоения, необходимо ознакомиться с перегрузкой операторов (overloaded operator), подробно рассматриваемой в главе 14.

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

Параметрами перегруженного оператора являются его операнды. Некоторые операторы, например присвоение, должны быть определены, как функции-члены. Когда оператор является функцией-членом, левый операнд связан с неявным параметром this (см. раздел 7.1.2). Правый операнд бинарного оператора, такого как присвоение, передается как явный параметр. Оператор присвоения копии получает аргумент того же типа, что и класс:

class Foo {

public:

 Foo& operator=(const Foo&); // оператор присвоения

 // ...

};

Для совместимости с оператором присвоения встроенных типов (см. раздел 4.4) операторы присвоения обычно возвращают ссылку на свой левый операнд. Следует также заметить, что библиотека обычно требует от типов, хранимых в контейнере, наличия операторов присвоения, возвращающих ссылку на левый операнд.

Операторы присвоения обычно должны возвращать ссылку на свой левый операнд.

Синтезируемый оператор присвоения копии

Подобно конструктору копий, компилятор создает синтезируемый оператор присвоения копии (synthesized assignment operator) для класса, если в нем не определен собственный. Аналогично конструктору копий, у некоторых классов синтезируемый оператор присвоения копии не подразумевает присвоения (раздел 13.1.6). В противном случае он присваивает значение каждой нестатической переменной-члена правого объекта соответствующей переменной-члену левого объекта с использованием оператора присвоения копии типа этой переменной. Массивы присваиваются поэлементно. Синтезируемый оператор присвоения копии возвращает ссылку на свой левый операнд.

Например, следующий код эквивалентен синтезируемому оператору присвоения копии класса Sales_data:

// эквивалент синтезируемого оператора присвоения копии

Sales_data&

Sales_data::operator=(const Sales_data &rhs) {

 bookNo = rhs.bookNo;         // вызов string::operator=

 units_sold = rhs.units_sold; // использует встроенное присвоение

 int revenue = rhs.revenue;   // использует встроенное

                              // присвоение double

 return *this;                // возвратить этот объект

}

Упражнения раздела 13.1.2

Упражнение 13.6. Что такое оператор присвоения копии? Когда он используется? Что делает синтезируемый оператор присвоения копии? Когда он синтезируется?

Упражнение 13.7. Что произойдет при присвоении одного объекта класса StrBlob другому? Что произойдет при присвоении объектов класса StrBlobPtr?

Упражнение 13.8. Напишите оператор присвоения для класса HasPtr из упражнения 13.5 раздела 13.1.1. Подобно конструктору копий, данный оператор присвоения должен копировать объект, на который указывает указатель рs.

13.1.3. Деструктор

Действие деструктора противоположно действию конструктора: конструкторы инициализируют нестатические переменные-члены объекта, а также могут выполнять другие действия; деструкторы осуществляют все действия, необходимые для освобождения использованных объектом ресурсов и удаления нестатических переменных-членов объекта.

1 ... 165 166 167 168 169 170 171 172 173 ... 297
Перейти на страницу:
На этой странице вы можете бесплатно скачать Язык программирования C++. Пятое издание - Стенли Липпман торрент бесплатно.
Комментарии