Constructors and member initializer lists
Конструкторы — это нестатические функции-члены , объявленные с использованием специального синтаксиса декларатора; они используются для инициализации объектов своих типов классов.
|
Конструктор не может быть корутиной . |
(since C++20) |
|
Конструктор не может иметь явный параметр объекта . |
(since C++23) |
Содержание |
Синтаксис
Конструкторы объявляются с использованием деклараторов функций-членов следующей формы:
class-name
(
parameter-list
(необязательно)
)
except
(необязательно)
attr
(необязательно)
|
|||||||||
| class-name | - | выражение-идентификатор, возможно, за которым следует список атрибутов , и (начиная с C++11) возможно, заключенное в пару круглых скобок | ||||||
| parameter-list | - | список параметров | ||||||
| except | - |
|
||||||
| attr | - | (начиная с C++11) список атрибутов |
Единственные спецификаторы, разрешенные в
спецификаторах объявления
объявления конструктора, это
friend
,
inline
,
constexpr
(начиная с C++11)
,
consteval
(начиная с C++20)
и
explicit
(в частности, возвращаемый тип не допускается). Обратите внимание, что
cv- и ref-квалификаторы
также не разрешены: семантика const и volatile для конструируемого объекта вступает в силу только после завершения конструктора наиболее производного класса.
Идентификаторное выражение class-name должно иметь одну из следующих форм:
- В объявлении friend , выражение-идентификатор является квалифицированным идентификатором , который именует конструктор .
- В противном случае, в объявлении члена, принадлежащем спецификации членов класса или шаблона класса:
-
- Для классов выражение-идентификатор представляет собой injected-class-name непосредственно охватывающего класса.
- Для шаблонов классов выражение-идентификатор представляет собой имя класса, которое обозначает current instantiation (до C++20) injected-class-name (начиная с C++20) непосредственно охватывающего шаблона класса.
- В противном случае, идентификаторное выражение является квалифицированным идентификатором, терминальный неквалифицированный идентификатор которого представляет собой injected-class-name своего контекста поиска .
Список инициализации членов класса
Тело
определения функции
любого конструктора класса
T
, перед открывающей фигурной скобкой составного оператора, может включать
список инициализации членов
, синтаксис которого представляет собой символ двоеточия
:
, за которым следует разделённый запятыми список одного или нескольких
инициализаторов-членов
, каждый из которых имеет следующий синтаксис:
| инициализатор члена инициализатор | (1) | ||||||||
| инициализатор класса инициализатор | (2) | ||||||||
инициализатор пакета классов
инициализатор
...
|
(3) | (начиная с C++11) | |||||||
|
(начиная с C++11) |
-
Прямой базовый класс или
виртуальный базовый класс
класса
T. В этом случае соответствующий подобъект базового класса прямо инициализируется с помощью initializer .
| member | - | идентификатор, который именует член данных |
| class | - | имя класса |
| class-pack | - | пакет, который раскрывается в ноль или более классов |
| initializer | - |
инициализатор
, который не начинается с
=
|
struct S { int n; S(int); // объявление конструктора S() : n(7) {} // определение конструктора: // ": n(7)" - это список инициализации // ": n(7) {}" - это тело функции }; S::S(int x) : n{x} {} // определение конструктора: ": n{x}" - это список инициализации int main() { S s; // вызывает S::S() S s2(10); // вызывает S::S(int) }
Объяснение
Конструкторы не имеют имен и не могут быть вызваны напрямую. Они вызываются при выполнении инициализации и выбираются согласно правилам инициализации. Конструкторы без спецификатора explicit являются преобразующими конструкторами . Конструкторы со спецификатором constexpr делают свой тип литеральным типом . Конструкторы, которые могут быть вызваны без аргументов, являются конструкторами по умолчанию . Конструкторы, принимающие другой объект того же типа в качестве аргумента, являются конструкторами копирования и конструкторами перемещения .
Перед тем как составной оператор, формирующий тело функции конструктора, начнет выполняться, завершается инициализация всех прямых базовых классов, виртуальных базовых классов и нестатических членов данных. Список инициализации членов - это место, где может быть задана нестандартная инициализация этих подобъектов. Для базовых классов, которые не могут быть инициализированы по умолчанию, и для нестатических членов данных, которые не могут быть инициализированы через default-инициализацию или через их default member initializer , если таковой имеется (since C++11) , таких как члены ссылочных и const-квалифицированных типов, должны быть указаны инициализаторы членов. (Заметьте, что default member initializers для нестатических членов данных инстанциаций шаблонов классов могут быть некорректными, если тип члена или инициализатор являются зависимыми.) (since C++11) Инициализация не выполняется для анонимных объединений или variant members , которые не имеют инициализатора члена или default member initializer (since C++11) .
Инициализаторы, где class указывает на виртуальный базовый класс, игнорируются при конструировании любого класса, который не является наиболее производным классом конструируемого объекта.
Имена, которые появляются в initializer , вычисляются в области видимости конструктора:
class X { int a, b, i, j; public: const int& r; X(int i) : r(a) // инициализирует X::r для ссылки на X::a , b{i} // инициализирует X::b значением параметра i , i(i) // инициализирует X::i значением параметра i , j(this->i) // инициализирует X::j значением X::i {} };
Исключения, которые выбрасываются из инициализаторов членов, могут быть обработаны с помощью блока try функции .
|
Если нестатический член данных имеет инициализатор по умолчанию для члена и также появляется в списке инициализации членов, то используется инициализатор члена, а инициализатор по умолчанию игнорируется: struct S { int n = 42; // default member initializer S() : n(7) {} // will set n to 7, not 42 }; |
(since C++11) |
Ссылочные члены не могут быть привязаны к временным объектам в списке инициализации членов:
struct A { A() : v(42) {} // Ошибка const int& v; };
Примечание: то же самое относится к default member initializer .
Операции во время конструирования и деструкции
Функции-члены (включая
виртуальные функции-члены
) могут быть вызваны для объекта в процессе конструирования или разрушения. Аналогично, объект в процессе конструирования или разрушения может быть операндом
typeid
или
dynamic_cast
.
Однако, если эти операции выполняются во время любого из следующих вычислений, поведение не определено:
|
(начиная с C++26) |
- оценка списка инициализации членов до завершения всех member-initializer ов для базовых классов
Делегирующий конструкторЕсли имя самого класса появляется как class-or-identifier в списке инициализации членов, то список должен состоять только из этого одного инициализатора члена; такой конструктор известен как делегирующий конструктор , а конструктор, выбранный единственным членом списка инициализации, является целевым конструктором . В этом случае целевой конструктор выбирается путем разрешения перегрузки и выполняется первым, затем управление возвращается в делегирующий конструктор и выполняется его тело. Делегирующие конструкторы не могут быть рекурсивными. class Foo { public: Foo(char x, int y) {} Foo(int y) : Foo('a', y) {} // Foo(int) delegates to Foo(char, int) }; Наследуемые конструкторыСмотрите using declaration . |
(since C++11) |
Порядок инициализации
Порядок инициализаторов членов в списке не имеет значения, фактический порядок инициализации следующий:
(Примечание: если бы порядок инициализации контролировался последовательностью в списках инициализации членов различных конструкторов, то деструктор не смог бы гарантировать, что порядок уничтожения является обратным порядку конструирования.)
Примечания
| Макрос тестирования возможностей | Значение | Стандарт | Возможность |
|---|---|---|---|
__cpp_delegating_constructors
|
200604L
|
(C++11) | Делегирующие конструкторы |
Пример
#include <fstream> #include <string> #include <mutex> struct Base { int n; }; struct Class : public Base { unsigned char x; unsigned char y; std::mutex m; std::lock_guard<std::mutex> lg; std::fstream f; std::string s; Class(int x) : Base{123}, // инициализация базового класса x(x), // x (член) инициализируется значением x (параметр) y{0}, // y инициализируется значением 0 f{"test.cc", std::ios::app}, // это происходит после инициализации m и lg s(__func__), // __func__ доступен, так как список инициализации является частью конструктора lg(m), // lg использует m, который уже инициализирован m{} // m инициализируется до lg, даже если он указан последним здесь {} // пустой составной оператор Class(double a) : y(a + 1), x(y), // x будет инициализирован до y, его значение здесь неопределено lg(m) {} // инициализатор базового класса отсутствует в списке, он // инициализируется по умолчанию (не то же самое, что если бы использовался Base(), что является value-init) Class() try // блок function try начинается до тела функции, которое включает список инициализации : Class(0.0) // делегирующий конструктор { // ... } catch (...) { // исключение произошло при инициализации } }; int main() { Class c; Class c1(1); Class c2(0.1); }
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 194 | C++98 |
синтаксис декларатора конструктора допускал
не более одного спецификатора функции (например, конструктор не мог быть объявлен inline explicit ) |
разрешено несколько
спецификаторов функции |
| CWG 257 | C++98 |
не было указано, должна ли абстрактный класс
предоставлять инициализаторы членов для своих виртуальных базовых классов |
указано как не требуемое
и такие инициализаторы членов игнорируются во время выполнения |
| CWG 263 | C++98 |
синтаксис декларатора конструктора
запрещал конструкторам быть дружественными функциями |
разрешено конструкторам
быть дружественными функциями |
| CWG 1345 | C++98 |
члены анонимных объединений без инициализаторов
членов по умолчанию были инициализированы по умолчанию |
они не инициализируются |
| CWG 1435 | C++98 |
значение "имя класса" в
синтаксисе декларатора конструктора было неясным |
изменен синтаксис на специализированный
синтаксис декларатора функции |
| CWG 1696 | C++98 |
ссылочные члены могли быть инициализированы временными объектами
(время жизни которых заканчивалось в конце конструктора) |
такая инициализация
является некорректной |
Ссылки
- Стандарт C++23 (ISO/IEC 14882:2024):
-
- 11.4.5 Конструкторы [class.ctor]
-
- 11.9.3 Инициализация базовых классов и членов [class.base.init]
- Стандарт C++20 (ISO/IEC 14882:2020):
-
- 11.4.4 Конструкторы [class.ctor]
-
- 11.10.2 Инициализация базовых классов и членов [class.base.init]
- Стандарт C++17 (ISO/IEC 14882:2017):
-
- 15.1 Конструкторы [class.ctor]
-
- 15.6.2 Инициализация базовых классов и членов [class.base.init]
- Стандарт C++14 (ISO/IEC 14882:2014):
-
- 12.1 Конструкторы [class.ctor]
-
- 12.6.2 Инициализация базовых классов и членов [class.base.init]
- Стандарт C++11 (ISO/IEC 14882:2011):
-
- 12.1 Конструкторы [class.ctor]
-
- 12.6.2 Инициализация базовых классов и членов [class.base.init]
- Стандарт C++98 (ISO/IEC 14882:1998):
-
- 12.1 Конструкторы [class.ctor]
-
- 12.6.2 Инициализация базовых классов и членов [class.base.init]