Конструкторы копирования
Конструктор копирования это конструктор, который можно вызвать с аргументом того же классового типа, который и копирует содержимое аргумента без изменения аргумента.
Содержание |
[править] Синтаксис
имя-класса ( список-параметров );
|
(1) | ||||||||
имя-класса ( список-параметров ) тело-функции
|
(2) | ||||||||
имя-класса ( список-одного-параметра ) = default;
|
(3) | (начиная с C++11) | |||||||
имя-класса ( список-параметров ) = delete;
|
(4) | (начиная с C++11) | |||||||
имя-класса :: имя-класса ( список-параметров ) тело-функции
|
(5) | ||||||||
имя-класса :: имя-класса ( список-одного-параметра ) = default;
|
(6) | (начиная с C++11) | |||||||
имя-класса | — | класс, конструктор копирования которого объявляется |
список-параметров | — | непустой список параметров, соответствующий всем следующим условиям:
|
список-одного-параметра | — | список параметров только одного параметра, который имеет тип T&, const T&, volatile T& или const volatile T& и не имеет аргумента по умолчанию |
тело-функции | — | тело функции конструктора копирования |
[править] Объяснение
struct X { X(X& other); // конструктор копирования // X(X other); // Ошибка: неверный тип параметра }; union Y { Y(Y& other, int num = 1); // конструктор копирования с несколькими параметрами // Y(Y& other, int num); // Ошибка: `num` не имеет аргумента по умолчанию };
Конструктор копирования вызывается всякий раз, когда объект инициализируется (прямой инициализацией или {{rlp|copy initialization|инициализации копированием}) из другого объекта того же типа (если только разрешение перегрузки не выбирает лучшее совпадение или вызов не пропускается), что включает в себя
- инициализацию: T a = b; или T a(b);, где b имеет тип
T
; - передача аргумента функции: f(a);, где a имеет тип
T
и f это void f(T t); - возврат функции: return a; внутри функции, такой как T f(), где a имеет тип
T
, который не имеет конструктора перемещения.
[править] Неявно объявленный конструктор копирования
Если для классового типа не предусмотрены пользовательские конструкторы копирования, компилятор всегда будет объявлять конструктор копирования как не explicit inline public элемент своего класса. Этот неявно объявленный конструктор копирования имеет форму T::T(const T&), если выполняются все следующие условия:
- каждый прямой и виртуальный базовый класс
B
классаT
имеет конструктор копирования, параметрами которого являются типами const B& или const volatile B&; - каждый нестатический элемент данных
M
изT
классового типа или массива классового типа имеет конструктор копирования, параметры которого являются типами const M& или const volatile M&.
Иначе неявно объявленный конструктор копирования имеет вид T::T(T&).
Из-за этих правил неявно объявленный конструктор копирования не может привязываться к аргументу volatile lvalue.
Класс может иметь несколько конструкторов копирования, например T::T(const T&) и T::T(T&).
Даже если присутствуют некоторые определяемые пользователем конструкторы копирования, пользователь всё равно может принудительно объявить неявное объявление конструктора копирования с помощью ключевого слова default. |
(начиная с C++11) |
Неявно объявленный (или заданный по умолчанию при первом объявлении) конструктор копирования имеет спецификацию исключения, как описано в спецификации динамических исключений (до C++17)спецификации noexcept (начиная с C++17).
[править] Неявно определённый конструктор копирования
Если неявно объявленный конструктор копирования не удалён, он определяется (то есть тело функции генерируется и компилируется) компилятором, если используется odr или необходим для константной оценки (начиная с C++11). Для типов объединения неявно определённый конструктор копирования копирует представление объекта (как в std::memmove). Для классовых типов, не являющихся объединением, конструктор выполняет полное поэлементное копирование базовых и нестатических элементов объекта в порядке их инициализации, используя прямую инициализацию.
Если это соответствует требованиям конструктора constexpr (до C++23)функции constexpr (начиная с C++23), сгенерированный конструктор копирования будет constexpr. Генерация неявно определённого конструктора копирования считается устаревшей, если |
(начиная с C++11) |
[править] Удалённый конструктор копирования
Неявно объявленный или явно заданный по умолчанию (начиная с C++11) конструктор копирования для класса T
не определён (до C++11)определяется как удалённый (начиная с C++11), если выполняется любое из следующих условий:
|
(начиная с C++11) |
-
T
имеет потенциально созданный подобъект классового типаM
(или, возможно, его многомерный массив), такой что
-
M
имеет деструктор, который удалён или (начиная с C++11) недоступен из конструктора копирования, или - разрешение перегрузки, применяемое для поиска конструктора копирования объекта
M
- не приводит к появлению пригодного для использования кандидата, или
- в случае, если подобъект является вариантным элементом, выбирает нетривиальную функцию.
-
Неявно объявленный конструктор копирования для класса |
(начиная с C++11) |
[править] Тривиальный конструктор копирования
Конструктор копирования для класса T
тривиален, если выполняются все следующие условия:
- он не предоставляется пользователем (то есть определяется неявно или используется по умолчанию);
-
T
не имеет виртуальных функций-элементов; -
T
не имеет виртуальных базовых классов; - конструктор копирования, выбранный для каждого прямого базового класса для
T
, тривиален; - конструктор копирования, выбранный для каждого элемента
T
нестатического классового типа (или массива классового типа), является тривиальным;
Тривиальный конструктор копирования для класса не объединения эффективно копирует каждый скалярный подобъект (включая, рекурсивно, подобъекты подобъектов и т.д.) аргумента и не выполняет никаких других действий. Однако байты заполнения не нужно копировать, и даже объектные представления скопированных подобъектов не обязательно должны быть одинаковыми, если их значения идентичны.
Объекты TriviallyCopyable можно копировать, копируя их объектные представления вручную, например с std::memmove. Все типы данных, совместимые с языком C (типы POD), копируются тривиально.
[править] Доступный конструктор копирования
Конструктор копирования доступен, если он либо объявлен пользователем, либо неявно объявлен и определён. |
(до C++11) |
Конструктор копирования доступен, если он не удалён. |
(начиная с C++11) (до C++20) |
Конструктор копирования доступен, если
|
(начиная с C++20) |
Тривиальность доступных конструкторов копирования определяет, является ли класс типом с неявным временем жизни и является ли класс тривиально копируемым типом.
[править] Примечание
Во многих ситуациях конструкторы копирования оптимизируются, даже если они будут производить наблюдаемые побочные эффекты, смотрите пропуск копирования.
[править] Пример
struct A { int n; A(int n = 1) : n(n) { } A(const A& a) : n(a.n) { } // пользовательский конструктор копирования }; struct B : A { // неявный конструктор по умолчанию B::B() // неявный конструктор копирования B::B(const B&) }; struct C : B { C() : B() { } private: C(const C&); // не копируемый, в стиле C++98 }; int main() { A a1(7); A a2(a1); // вызывает конструктор копирования B b; B b2 = b; A a3 = b; // преобразование в A& и конструктор копирования volatile A va(10); // A a4 = va; // ошибка компиляции C c; // C c2 = c; // ошибка компиляции }
[править] Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
CWG 1353 | C++98 | условия, при которых неявно объявленные конструкторы копирования не определены, не учитывали типы многомерных массивов | эти типы учтены |
CWG 2094 | C++11 | volatile элементы делают копирование нетривиальным (CWG 496) | тривиальность не имеет эффекта |
CWG 2171 | C++11 | X(X&) = default было нетривиальным | сделано тривиальным |