ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 13.12.2020
Просмотров: 4299
Скачиваний: 28
Глава 15. Конструкторы и деструкторы
201
Отсюда следует, что если в конструкторе копирования выполня-
ется тривиальное копирование данных-членов класса, то такой
конструктор копирования определять не нужно.
Ниже приведена реализация конструктора копирования для де-
монстрационного класса, который требует определения конст-
руктора копирования.
class Demo
{
unsigned size;
public:
int* p;
// конструкторы
Demo(unsigned n): size(n), p(new int[size]) {}
Demo(const Demo& d);
// деструктор
~Demo() { delete[] p; }
};
// реализация конструктора копирования
Demo::Demo(const Demo& d): size(d.size), p(new int[size])
{
for (unsigned i = 0; i < size; ++i)
p[i] = d.p[i];
}
Конструктор копирования вызывается явно в случае явной ини-
циализации объекта другим объектом этого же класса. Например:
Demo d(3);
d.p[0] = 0;
d.p[1] = 1;
Demo c(d); // вызывается конструктор копирования
Конструктор копирования вызывается неявно в трех случаях:
при передаче объекта в тело функции по значению;
при возврате объекта из функции по значению;
при инициализации объекта другим объектом этого же класса
при помощи оператора присваивания.
Часть II. Язык программирования С++
202
Например, в следующем примере вызывается конструктор копи-
рования, а не оператор присваивания:
Demo d(3);
d.p[0] = 0;
d.p[1] = 1;
Demo c = d; // вызывается конструктор копирования
Отсюда следует, что нетривиальный конструктор копирования
нужно обязательно определить, иначе возможны ошибки во вре-
мя исполнения программы.
Инициализацию через копирование можно запретить, если по-
местить конструктор копирования в раздел
private
.
15.5. Явный вызов конструкторов
Конструктор может вызываться явно в двух случаях:
в списке инициализации;
в выражении.
Во втором случае конструктор рассматривается как оператор,
возвращающий анонимный константный объект соответствую-
щего типа. Например:
int n = 1 + int(1.7); // n = 2
Отсюда следует, что конструкторы могут использоваться в выра-
жения для явного преобразования типов данных.
Конструктор с одним параметром рассматривается компилятором
как определенный пользователем оператор преобразования ти-
пов. То есть компилятор может использовать этот конструктор
для неявного преобразования типов в выражении.
В листинге 15.3 приведен пример использования конструктора
с одним параметром как оператора преобразования типа.
Листинг 15.3. Использование конструктора в качестве оператора
преобразования типа
#include <iostream>
using namespace std;
Глава 15. Конструкторы и деструкторы
203
struct Demo
{
int n;
public:
Demo(int _n = 0): n(_n) {}
operator+=(const Demo& d) { n += d.n; }
};
int main()
{
Demo d;
d += 10;
cout << d.n << endl;
return 0;
}
Чтобы запретить такое использование конструктора с одним па-
раметром, этот конструктор объявляется как
явный
при помощи
спецификатора
explicit
. Например:
struct Demo
{
int n;
public:
explicit Demo(int _n = 0): n(_n) {}
operator+=(const Demo& d) { n += d.n; }
};
Тогда приведенный ниже оператор присваивания вызовет ошиб-
ку компиляции.
Demo d;
d += 10; // ошибка компиляции
15.6. Деструкторы
Деструктор
— это функция-член класса, имя которой начинает-
ся с символа
~
, за которым без пробелов следует имя класса. Де-
структор вызывается компилятором
неявно при уничтожении
Часть II. Язык программирования С++
204
объекта
. Деструктор предназначен для освобождения ресурсов,
захваченных во время жизни объекта.
Например, в классе
Demo
из
разд. 15.4
деструктор был определен
следующим образом:
~Demo() { delete[] p; } // деструктор
Этот деструктор освобождает память, распределенную объекту
в конструкторе.
Если ресурсы освобождать не нужно, то деструктор не пишется.
В этом случае компилятор генерирует деструктор по умолчанию,
который выполняет следующие действия: сначала вызывает дест-
рукторы данных-членов класса, а затем — деструкторы базовых
классов.
Деструктор должен удовлетворять следующим требованиям:
деструктор не может иметь параметров;
деструктор не может возвращать значения;
деструктор не может быть объявлен с квалификаторами
const
или
volatile
;
деструктор не может быть объявлен со спецификатором
static
.
Так как деструктор не может иметь параметров, то его нельзя пе-
регрузить. Следовательно, деструктор должен быть всегда один.
Деструкторы вызываются неявно в следующих случаях:
для
static
объектов при завершении программы;
для локальных объектов при выходе из блока, в котором эти
объекты определены;
при применении оператора
delete
к указателю на объект;
при раскрутке стека во время обработки исключения.
Деструктор вызывается явно при уничтожении объекта, который
был создан размещающим оператором
new
. В других случаях вы-
зывать деструктор явно не нужно, т. к. в этом случае объект не
уничтожается и при выходе из блока деструктор будет вызван
еще раз. Чтобы удалить объект до выхода из блока, нужно помес-
тить этот объект в искусственный блок.
Глава 15. Конструкторы и деструкторы
205
15.7. Исключения в деструкторах
Если во время раскрутки стека
(см. разд. 13.5)
выброшено новое
исключение, то механизм обработки исключений вызывает
функцию
terminate
, которая завершает программу посредством
вызова функции
abort
. Поэтому выбрасывать исключения из
деструкторов не рекомендуется, т. к. они могут вызываться во
время раскрутки стека. Чтобы определить, обрабатывается ли
в данный момент исключение, можно использовать функцию
uncaught_exception
, которая определена в заголовочном файле
exception. Использование этой функции рассмотрено в
разд. 36.6
.