|
Объекты класса могут копироваться двумя способами: либо присваиванием
($$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 не копируется.
Копирование одного объекта в другой с помощью стандартной
операции копирования или стандартного конструктора копирования
не изменяет структуру обоих объектов. Приведем пример:
struct s { virtual f(); // ... }; struct ss : public s { f(); // ... }; void f() { s a; ss b; a = b; // на самом деле выполняется a.s::operator=(b) b = a; // ошибка a.f(); // вызов s::f b.f(); // вызов ss::f (s&)b = a; // присваивание a b // на самом деле выполняется ((s&)b).s::operator=(a) b.f(); // все еще вызов ss::f }
Вызов a.f() приведет к вызову s::f() (как и должно быть для объекта класса s ($$R.10.2)), а вызов b.f() приведет к вызову ss::f() ( как и должно быть для объекта класса ss).