Выбрать главу

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);

 X(const X&, int = 1);

};

X a(1); // вызов X(int)

X b(a,0); // вызов X(const X&,int)

X c = b; // вызов X(const X&,int)

Присваивание объектов класса X определяется через функцию X::operator=(const X&). Это означает (§R.12.3), что объекты производного класса можно присваивать объектам общего базового класса, например:

class X {

public:

 int b;

};

class Y: public X {

public:

 int c;

};

void f()

{

 X x1;

 Y y1;

 x1 = y1; // нормально

 y1 = x1; // ошибка

}

В этом примере y1.b присваивается x1.b, а x1.c не копируется.

Копирование одного объекта в другой с помощью стандартной операции копирования или стандартного конструктора копирования не изменяет структуру обоих объектов. Приведем пример: