Конструкторы и списки инициализации членов
Конструкторы — это нестатические функции-члены, объявленные с использованием специального синтаксиса декларатора; они используются для инициализации объектов типов классов.
| Конструктор не может быть корутиной. | (начиная с C++20) |
| Конструктор не может иметь явный параметр объекта. | (начиная с 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 должно иметь одну из следующих форм:
- В объявлении друга идентификаторное выражение является квалифицированным идентификатором, который обозначает конструктор.
- В противном случае, в объявлении члена, принадлежащем спецификации членов класса или шаблона класса:
- Для классов идентификаторное выражение является внедрённым именем класса непосредственно охватывающего класса.
- Для шаблонов классов идентификаторное выражение является именем класса, обозначающим текущую специализацию(до C++20)внедрённым именем класса(начиная с C++20) непосредственно охватывающего шаблона класса.
- В противном случае идентификаторное выражение является квалифицированным идентификатором, конечный неквалифицированный идентификатор которого представляет собой внедрённое имя класса в контексте его поиска.
Список инициализации членов
Тело определения функции любого конструктора перед открывающей фигурной скобкой составного оператора может содержать список инициализации членов, синтаксис которого представляет собой символ двоеточия :, за которым следует разделённый запятыми список из одного или нескольких элементов member-initializer, каждый из которых имеет следующий синтаксис:
class-or-identifier ( expression-list (необязательно) ) | (1) | |
| class-or-identifier braced-init-list | (2) | (начиная с C++11) |
parameter-pack ... | (3) | (начиная с C++11) |
| class-or-identifier | - | любой идентификатор, обозначающий нестатический член-данные, либо любое имя типа, обозначающее сам класс (для делегирующих конструкторов) или прямой либо виртуальный базовый класс. |
| expression-list | - | возможно, пустой список аргументов, разделённых запятыми, которые передаются конструктору базового класса или члена |
| braced-init-list | - | список инициализаторов в фигурных скобках |
| parameter-pack | - | имя пакета параметров вариативного шаблона |
struct S
{
int n;
S(int); // constructor declaration
S() : n(7) {} // constructor definition:
// ": n(7)" is the initializer list
// ": n(7) {}" is the function body
};
S::S(int x) : n{x} {} // constructor definition: ": n{x}" is the initializer list
int main()
{
S s; // calls S::S()
S s2(10); // calls S::S(int)
}
Пояснение
У конструкторов нет имён, и их нельзя вызывать напрямую. Они вызываются при выполнении инициализации и выбираются согласно правилам инициализации. Конструкторы без спецификатора explicit являются преобразующими конструкторами. Конструкторы со спецификатором constexpr делают свой тип литеральным типом. Конструкторы, которые можно вызвать без аргументов, являются конструкторами по умолчанию. Конструкторы, принимающие в качестве аргумента другой объект того же типа, являются конструкторами копирования и конструкторами перемещения.
До начала выполнения составного оператора, образующего тело функции конструктора, завершается инициализация всех прямых базовых классов, виртуальных базовых классов и нестатических членов-данных. Список инициализации членов — это место, где можно задать нестандартную инициализацию этих подобъектов. Для базовых классов, которые нельзя инициализировать по умолчанию, и нестатических членов-данных, которые нельзя инициализировать посредством инициализации по умолчанию или их инициализатора по умолчанию для члена, если он есть(начиная с C++11), например членов ссылочного и const-квалифицированного типов, инициализаторы членов должны быть заданы. (Обратите внимание, что инициализаторы по умолчанию для нестатических членов-данных экземпляров шаблона класса могут быть некорректными, если тип члена или инициализатор являются зависимыми.)(начиная с C++11) Для анонимных объединений или вариантных членов, у которых нет инициализатора члена или инициализатора по умолчанию для члена(начиная с C++11), инициализация не выполняется.
Инициализаторы, в которых class-or-identifier обозначает виртуальный базовый класс, игнорируются при конструировании любого класса, который не является наиболее производным классом создаваемого объекта.
Имена, встречающиеся в expression-list или braced-init-list, вычисляются в области видимости конструктора:
class X
{
int a, b, i, j;
public:
const int& r;
X(int i)
: r(a) // initializes X::r to refer to X::a
, b{i} // initializes X::b to the value of the parameter i
, i(i) // initializes X::i to the value of the parameter i
, j(this->i) // initializes X::j to the value of X::i
{}
};
Исключения, возникающие в инициализаторах членов, могут обрабатываться с помощью блока try функции.
| Если у нестатического члена-данных есть инициализатор по умолчанию для члена и он также указан в списке инициализации членов, используется инициализатор члена, а инициализатор по умолчанию для члена игнорируется: struct S
{
int n = 42; // default member initializer
S() : n(7) {} // will set n to 7, not 42
};
| (начиная с C++11) |
Ссылочные члены нельзя связывать с временными объектами в списке инициализации членов:
struct A
{
A() : v(42) {} // Error
const int& v;
};
Примечание: то же относится к инициализатору по умолчанию для члена.
Операции во время конструирования и разрушения
Для объекта, который конструируется или разрушается, можно вызывать функции-члены (включая виртуальные функции-члены). Аналогичным образом объект, который конструируется или разрушается, может быть операндом 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. | (начиная с 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}, // initialize base class
x(x), // x (member) is initialized with x (parameter)
y{0}, // y initialized to 0
f{"test.cc", std::ios::app}, // this takes place after m and lg are initialized
s(__func__), // __func__ is available because init-list is a part of constructor
lg(m), // lg uses m, which is already initialized
m{} // m is initialized before lg even though it appears last here
{} // empty compound statement
Class(double a) : y(a + 1),
x(y), // x will be initialized before y, its value here is indeterminate
lg(m)
{} // base class initializer does not appear in the list, it is
// default-initialized (not the same as if Base() were used, which is value-init)
Class()
try // function try block begins before the function body, which includes init list
: Class(0.0) // delegate constructor
{
// ...
}
catch (...)
{
// exception occurred on initialization
}
};
int main()
{
Class c;
Class c1(1);
Class c2(0.1);
}
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были применены ретроспективно к ранее опубликованным стандартам C++.
| DR | Применён к | Поведение согласно опубликованной редакции | Корректное поведение |
|---|---|---|---|
| CWG 194 | C++98 | синтаксис декларатора конструктора допускал не более одного спецификатора функции (например, конструктор нельзя было объявить как inlineexplicit) | разрешено несколько спецификаторов функции |
| 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]
См. также
- устранение копирования
- преобразующий конструктор
- копирующее присваивание
- конструктор копирования
- конструктор по умолчанию
- деструктор
explicit- инициализация
- перемещающее присваивание
- конструктор перемещения
new
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/constructor