Initialization
Инициализация переменной предоставляет её начальное значение во время создания.
Начальное значение может быть предоставлено в секции инициализатора декларатора или в new expression . Это также происходит во время вызовов функций: параметры функций и возвращаемые значения также инициализируются.
Содержание |
Инициализаторы
Для каждого декларатора инициализатор (если присутствует) может быть одним из следующих:
=
выражение
|
(1) | ||||||||
= {}
= {
список-инициализации
}
= {
список-инициализации-с-обозначениями
}
|
(2) |
(since C++20) |
|||||||
(
список-выражений
)
(
список-инициализации
)
|
(3) |
(until C++11)
(since C++11) |
|||||||
{}
{
список-инициализации
}
{
список-инициализации-с-обозначениями
}
|
(4) |
(since C++11)
(since C++11) (since C++20) |
|||||||
| expression | - | любое выражение (кроме неподобных comma expressions ) |
| expression-list | - | список выражений, разделённых запятыми (кроме неподобных comma expressions) |
| initializer-list | - | список инициализаторов, разделённых запятыми (см. ниже) |
| designated-initializer-list | - | список designated initializer clauses , разделённых запятыми |
Инициализирующее выражение
может быть одним из следующих:
| expression | (1) | ||||||||
{}
|
(2) | ||||||||
{
initializer-list
}
|
(3) | ||||||||
{
designated-initializer-list
}
|
(4) | (начиная с C++20) | |||||||
Синтаксисы (2-4) в совокупности называются brace-enclosed initializer list .
Семантика инициализаторов
Если для объекта не указан инициализатор, объект default-initialized . Если для reference не указан инициализатор, программа является некорректной.
Если инициализатор, указанный для объекта, имеет вид ( ) (не может встречаться в деклараторах из-за синтаксических ограничений), объект подвергается value-инициализации . Если инициализатор, указанный для ссылки, имеет вид ( ) , программа является некорректной.
Семантика инициализаторов следующая:
- Если инициализируемая сущность является ссылкой, см. инициализация ссылок .
-
В противном случае инициализируемая сущность является объектом. При заданном типе объекта как
T:
-
- Если инициализатор имеет синтаксис (1) , объект copy-initialized .
|
(до C++11) |
|
(начиная с C++11) |
-
- Если инициализатор имеет синтаксис (3) , объект direct-initialized .
#include <string> std::string s1; // инициализация по умолчанию std::string s2(); // НЕ инициализация! // фактически объявляет функцию «s2» // без параметров, возвращающую std::string std::string s3 = "hello"; // копирующая инициализация std::string s4("hello"); // прямая инициализация std::string s5{'a'}; // списочная инициализация (начиная с C++11) char a[3] = {'a', 'b'}; // агрегатная инициализация // (часть списочной инициализации с C++11) char& c = a[0]; // инициализация ссылки
Нелокальные переменные
Все нелокальные переменные со статической продолжительностью хранения инициализируются как часть запуска программы, до начала выполнения функции main (если не отложены, см. ниже). Все нелокальные переменные с продолжительностью хранения thread-local инициализируются как часть запуска потока, до начала выполнения функции потока. Для обоих этих классов переменных инициализация происходит в два отдельных этапа:
Статическая инициализация
Существует две формы статической инициализации:
На практике:
- Константная инициализация обычно применяется на этапе компиляции. Предварительно вычисленные представления объектов сохраняются как часть образа программы. Если компилятор этого не делает, он всё равно должен гарантировать, что инициализация произойдёт до любой динамической инициализации.
-
Переменные, подлежащие нулевой инициализации, размещаются в сегменте
.bssобраза программы, который не занимает места на диске и обнуляется операционной системой при загрузке программы.
Динамическая инициализация
После завершения всей статической инициализации, динамическая инициализация нелокальных переменных происходит в следующих ситуациях:
|
2)
Частично-упорядоченная динамическая инициализация
, которая применяется ко всем inline-переменным, не являющимся неявно или явно инстанцируемой специализацией. Если частично-упорядоченная V определена до упорядоченной или частично-упорядоченной W в каждой единице трансляции, инициализация V упорядочена до инициализации W (или происходит-до, если программа запускает поток).
|
(since C++17) |
Если инициализация нелокальной переменной со статической длительностью хранения или длительностью хранения потока завершается через исключение, std::terminate вызывается.
Ранняя динамическая инициализация
Компиляторам разрешено инициализировать динамически инициализируемые переменные как часть статической инициализации (по сути, на этапе компиляции), если выполняются оба следующих условия:
Из-за приведенного выше правила, если инициализация некоторого объекта
o1
ссылается на объект в области видимости пространства имен
o2
, который потенциально требует динамической инициализации, но определен позже в той же единице трансляции, не определено, будет ли использовано значение полностью инициализированного
o2
(поскольку компилятор выполнил инициализацию
o2
во время компиляции) или будет использовано значение
o2
, лишь прошедшее нулевую инициализацию.
inline double fd() { return 1.0; } extern double d1; double d2 = d1; // не определено: // динамически инициализируется в 0.0, если d1 динамически инициализирована, или // динамически инициализируется в 1.0, если d1 статически инициализирована, или // статически инициализируется в 0.0 (поскольку это было бы её значение // если бы обе переменные были динамически инициализированы) double d1 = fd(); // может быть инициализирована статически или динамически в 1.0
Отложенная динамическая инициализация
Является реализационно-определенным, происходит ли динамическая инициализация happens-before первого оператора функции main (для статических переменных) или начальной функции потока (для thread-local переменных), или откладывается для выполнения после.
Если инициализация неинлайновой переменной (since C++17) отложена до выполнения после первого оператора main/функции потока, она происходит перед первым ODR-использованием любой переменной со статической/потоковой длительностью хранения, определённой в той же единице трансляции, что и инициализируемая переменная. Если из данной единицы трансляции не используется ODR-способом ни одна переменная или функция, нелокальные переменные, определённые в этой единице трансляции, могут никогда не быть инициализированы (это моделирует поведение динамической библиотеки по требованию). Однако, пока что-либо из единицы трансляции используется ODR-способом, все нелокальные переменные, чья инициализация или уничтожение имеют побочные эффекты, будут инициализированы, даже если они не используются в программе.
|
Если инициализация inline-переменной отложена, она происходит перед первым ODR-использованием этой конкретной переменной. |
(since C++17) |
// ============ // == Файл 1 == #include "a.h" #include "b.h" B b; A::A() { b.Use(); } // ============ // == Файл 2 == #include "a.h" A a; // ============ // == Файл 3 == #include "a.h" #include "b.h" extern A a; extern B b; int main() { a.Use(); b.Use(); } // Если a инициализируется до входа в main, b может остаться неинициализированным // в точке, где A::A() его использует (поскольку динамическая инициализация // недетерминированно упорядочена между единицами трансляции) // Если a инициализируется в какой-то момент после первого оператора main (который odr-использует // функцию, определенную в Файле 1, принудительно запуская его динамическую инициализацию), // тогда b будет инициализирован до его использования в A::A
Статические локальные переменные
Для инициализации локальных (то есть, блочной области видимости) статических и thread-local переменных см. static block variables .
Инициализатор не допускается в блочном объявлении переменной с внешней или внутренней линковкой . Такое объявление должно появляться с extern и не может быть определением.
Члены класса
Нестатические члены данных могут быть инициализированы с помощью списка инициализации членов или с помощью инициализатора члена по умолчанию .
Примечания
Порядок уничтожения нелокальных переменных описан в std::exit .
Отчёты о дефектах
Следующие отчеты об изменениях в поведении, являющиеся дефектными, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| CWG 270 | C++98 |
порядок инициализации статических членов данных
шаблонов классов был неопределен |
определен как неупорядоченный, за исключением
явных специализаций и определений |
| CWG 441 | C++98 |
нелокальные ссылки со статической продолжительностью хранения
не всегда инициализировались до динамических инициализаций |
рассматриваются как статическая инициализация, всегда
инициализируются до динамических инициализаций |
| CWG 1415 | C++98 |
объявление переменной
extern
в области видимости блока
могло быть определением |
запрещено (инициализатор не допускается
в таких объявлениях) |
| CWG 2599 | C++98 |
было неясно, является ли вычисление аргументов функции
в инициализаторе частью инициализации |
это часть инициализации |
Смотрите также
- копиевая элизия
- преобразующий конструктор
- конструктор копирования
- конструктор по умолчанию
-
explicit - перемещающий конструктор
-
new
|
Документация C
для
Инициализации
|