Справочное руководство по C++ - Бьярн Страустрап
Шрифт:
Интервал:
Закладка:
(3) X имеет конструктор и каждый из них задает инициализатор-ctor (§R.12.6.2) для члена M.
В случае 2 при создании составного объекта вызывается стандартный конструктор. Если член составного объекта имеет деструктор, то он вызывается при уничтожении составного объекта.
Конструкторы для нелокальных статических объектов вызываются в том порядке, в каком они идут в тексте программы, деструкторы вызываются в обратном порядке, см. также §R.3.4, §R.6.7, §R.9.4.
R.12.6.2 Инициализация членов и базовых классов
В определении конструктора можно задать инициализацию прямых базовых классов и членов, не наследуемых из базовых классов. Это особенно полезно для тех объектов, констант и ссылок, для которых различаются семантики присваивания и инициализации. Конструкция инициализатор-ctor имеет вид
инициализатор-ctor:
: список-инициализаторов-членов
список-инициализаторов-членов:
инициализатор-члена
инициализатор-члена , список-инициализаторов-члена
инициализатор-члена:
полное-имя-класса ( список-выражений opt )
идентификатор
Список параметров используется для инициализации нестатических членов или объектов базового класса. Это единственный способ инициализации нестатических членов, являющихся ссылками или объектами типа const, например:
struct B1 { B1(int); /*… */ };
struct B2 { B2(int); /*… */ };
struct D: B1, B2 {
D(int);
B1 b;
const c;
};
D::D(int a): B2(a+1), B1(a+2), c(a+3), b(a+4)
{/*… */}
D d(10);
В начале инициализируются базовые классы в порядке их описания (независимо от порядка инициализаторов-членов), затем по той же схеме инициализируются члены, и наконец выполняется тело D::D() (§R.12.1). Порядок описания выдерживается для того, чтобы гарантировать, что вложенные объекты и члены будут уничтожаться в порядке, обратном их инициализации.
Особый случай представляют виртуальные базовые классы. Они создаются прежде, чем любой невиртуальный базовый класс и в том же порядке, в каком появляются при обходе снизу и слева-направо ацикличного направленного графа базовых классов. Порядок "слева-направо" - это тот, в котором имена базовых классов задаются при описании в производном классе.
Полным называется объект, который не является вложенным объектом, представляющим некоторый базовый класс. Класс такого объекта называют наибольшим производным классом объекта. Все вложенные объекты виртуальных базовых классов инициализируются с помощью конструктора наибольшего производного класса. Если в конструкторе наибольшего производного класса не задан инициализатор-члена для виртуального базового класса, тогда этот виртуальный базовый класс должен иметь стандартный конструктор,либо не иметь никакого конструктора. Всякий инициализатор-члена для виртуального базового класса, заданный не в конструкторе класса полного объекта, игнорируется. Приведем пример:
class V {
public:
V();
V(int);
//…
};
class A: public virtual V {
public:
A();
A(int);
//…
};
class B: public virtual V {
public:
B();
B(int);
//…
};
class C: public A, public B, private virtual V {
public:
C();
C(int);
//…
};
A::A(int i): V(i) {/*… */}
B::B(int i) {/*… */}
C::C(int i) {/*… */}
V v(1); // use V(int)
A a(2); // use V(int)
B b(3); // use V()
C c(4); // use V()
Инициализатор-члена вычисляется в области видимости конструктора, в котором он появился. Например, в следующем фрагменте
class X {
int a;
public:
const int& r;
X()::r(a) {}
};
X::r инициализируется для каждого объекта класса X ссылкой на X::a.
R.12.7 Конструкторы и деструкторы
В конструкторах и деструкторах можно вызывать функцию-член. Отсюда следует, что можно вызывать (непосредственно или косвенно) виртуальные функции. Вызываемая функция должна быть определена в классе самого конструктора или деструктора или в базовых классах, но не должна быть функцией, которая их подавляет в производном классе. Этим обеспечивается то, что еще несозданные объекты не будут использованы при выполнении конструктора или деструктора. Рассмотрим пример:
class X {
public:
virtual void f();
X() { f(); } // вызов X::f()
~X() { f(); } // вызов X::f()
};
class Y: public X {
int& r;
public:
void f()
{
r++; // беда, если `r' не инициализировано
}
Y(int& rr)::r(rr) {}
};
Результат непосредственного или косвенного вызова из конструктора чистой виртуальной функции для инициализируемого объекта неопределен, если только явно не использовано уточнение имени функции (§R.10.3).
R.12.8 Копирование объектов класса
Объекты класса могут копироваться двумя способами: либо присваиванием (§R.5.17), либо инициализацией (§R.12.1, §R.8.4), которая может происходить при передаче параметров (§R.5.2.2) или результата функции (§R.6.6.3). Для класса X эти две операции концептуально реализуются как операция присваивания и конструктор копирования (§R.12.1). В программе можно определить или одну из них, или обе. Если пользователь не определил их в программе, то они будут для всех членов класса X определяться соответственно как присваивание по членам и инициализация по членам.
Если все базовые классы и все члены класса X имеют конструктор копирования, в котором допустимы в качестве параметра объекты типа const, то порождаемый конструктор копирования для X будет иметь единственный параметр типа const X& и записываться так:
X::X(const X&)
Иначе, у него будет единственный параметр типа X&:
X::X(X&)
и инициализация копированием объектов типа const класса X будет невозможна.
Аналогично, если все базовые классы и члены класса X имеют операцию присваивания, допускающую параметры типа const, тогда порождаемая для X операция присваивания будет иметь единственный параметр типа const X& и записываться так:
X& X::operator=(const X&)
Иначе, у нее будет единственный параметр типа X&:
X& X::operator=(X&)
и присваивание копированием объектов класса X типа const будет невозможно. Стандартная операция присваивания возвращает ссылку на объект, который нужно было копировать.
Объекты, представляющие виртуальные базовые классы, будут инициализироваться только один раз с помощью порождаемого конструктора копирования. Объекты, представляющие виртуальные базовые классы, допускают присваивания им только один раз с помощью порождаемой операции присваивания.
Присваивание по членам и инициализация по членам означают следующее: если класс X имеет в качестве члена класс M, для реализации присваивания и инициализации члена используются операции присваивания в M и конструктор копирования M соответственно. Если класс имеет член типа const, или член, являющийся ссылкой, или член или базовый класс такого класса, где функция operator=() является частной, то для него стандартная операция присваивания не может быть создана. Аналогично, если член или базовый класс класса M имеет частный конструктор копирования, то стандартный конструктор копирования для такого класса не может быть создан.
Пока не появится необходимость в определении, стандартные присваивание и конструктор копирования будут только описаны (т.е. не будет создано тело функции). Иными словами, функция X::operator=() будет порождена только тогда, когда нет явного описания операций присваивания, а объект класса X присваивается объекту класса X или объекту класса, производного от X, или вычисляется адрес функции X::operator=(). Аналогичная ситуация с инициализацией.
Если присваивание и конструктор копирования описаны неявно, то они будут общими функциями-членами и операция присваивания для класса X определяется таким образом, что ее результатом является ссылка типа X& на объект, которому присваивают.
Если в классе X есть функция X::operator=(), параметром которой является сам класс X, то стандартное присваивание не будет порождаться. Если в классе определен какой-либо конструктор копирования, то стандартный конструктор копирования не будет порождаться. Приведем пример:
class X {
//…
public:
X(int);