разделенные запятыми).
2. Затем вызываются конструкторы переменных класса в порядке их объявления в объявлении
класса.
3. После того как будут сконструированы все базовые классы и переменные, выполняется тело
вашего конструктора.
Описанный порядок применяется рекурсивно, то есть первым конструируется первый базовый класс
первого базового класса... и т. д. Он не зависит от порядка, указанного в списке инициализации членов.
Если бы дело обстояло иначе, для разных перегруженных конструкторов мог бы использоваться
разный порядок конструирования. Тогда компилятору было бы трудно гарантировать, что деструкторы
будут вызываться в порядке, обратном порядку вызова конструкторов.
Конструкторы копий
Конструктор копий (copy constructor) определяется специальной сигнатурой:
class Foo {
public:
Foo(const
Foo&);
};
Foo::Foo(const Foo& f)...
Конструктор копий предназначен для создания копий объектов. Эта задача может возникнуть в самых
разных обстоятельствах.
void Fn(Foo f) {...}
void Gn(Foo& f) {...}
Foo f;
Foo f1(f);
Foo f2 = f;
// Конструирование, а не присваивание!
Fn(f); //
Вызывает конструктор копий для передачи по назначению
const Foo f3;
Gn(f3);
//
Конструктор копий используется
//
для создания неконстантной копии
Давайте внимательно рассмотрим этот фрагмент. Строка
Foo f1(f);
создает новый экземпляр класса
Foo, передавая другой экземпляр класса
Foo
в качестве аргумента. Это всегда можно сделать, если
36
класс
Foo
не содержит чисто виртуальных функций. Не важно, объявили ли вы свой собственный
конструктор копий; если нет, компилятор построит его за вас. Не важно, есть ли в
Foo
другие
пользовательские конструкторы; в отличие от конструкторов без аргументов, конструктор копий
доступен всегда.
Строка
Foo f2 = f
выглядит как присваивание из-за присутствия оператора
=
, но на самом деле это
альтернативный вариант вызова конструктора копий. Чтобы понять, чем присваивание отличается от
инициализации, спросите себя: «Был ли объект сконструирован заранее или же его создание является
частью команды?» Если объект уже существует, вы имеете дело с присваиванием. Если он создается на
месте, как в пашем примере, используется конструктор копий.
При вызове функции
Fn()
происходит передача по значению копии
Foo
. Конструктор копий
используется для создания временной копии, существующей лишь во время выполнения
Fn()
. После
этого вызывается деструктор копии, который уничтожает ее.
Вызов функции
Gn()
, вероятно, ошибочен, и хороший компилятор прочитает вам суровую нотацию о
стиле программирования на C++ — что-нибудь вроде:
«Создается временная неконстантная копия — поучись программировать, тупица!» По крайней мере,
со мной компиляторы обычно поступают именно так. Проблема заключается в том, что аргумент
передается по ссылке, однако фактический аргумент является константным, а формальный — нет. Все
изменения аргумента внутри
Gn()
вносятся в копию, а не в оригинал.
В создаваемом компилятором конструкторе копий по умолчанию используется строго определенная
последовательность вызова конструкторов копий базовых классов и переменных класса.
1. Конструкторы копий базовых классов вызываются в том порядке, в котором они объявлены в
списке наследования.
2. Конструкторы копий переменных вызываются в том порядке, в котором они объявлены в
объявлении класса.
Описанный порядок применяется рекурсивно, то есть первым копируется первый базовый класс
первого базового класса... и т. д. Звучит знакомо, не правда ли? Тот же порядок, что и для любого
другого конструктора.
С конструкторами копий, в отличие от всех остальных, компилятор ведет себя гордо и ревниво. Если
вы перегрузите конструктор копий для некоторого класса, компилятор, фигурально выражаясь,
умывает руки и отправляется домой. При отсутствии явного вызова конструкторов копий базовых
классов и переменных класса в списке инициализации членов вашего собственного конструктора
копий компилятор будет использовать конструктор без аргументов для инициализации базовых
классов и переменных.
class Foo {...};
class Bar : public Foo {
private:
Foo
f;
public:
Bar(const
Bar&);
};
// Вероятно, ошибка
Bar::Bar(const Bar& b)
{
//
Стоп! Нет списка инициализации членов
//
Будут использованы конструкторы без аргументов
//
базового класса и переменной
}
// Вероятно, ошибки нет
Bar::Bar(const Bar& b) : Foo(b), f(b.f) {...}
Do'stlaringiz bilan baham: |