Namespaces
Variants

Initialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

Инициализация переменной предоставляет её начальное значение во время создания.

Начальное значение может быть предоставлено в секции инициализатора декларатора или в new expression . Это также происходит во время вызовов функций: параметры функций и возвращаемые значения также инициализируются.

Содержание

Инициализаторы

Для каждого декларатора инициализатор (если присутствует) может быть одним из следующих:

= выражение (1)
= {}
= { список-инициализации }
= { список-инициализации-с-обозначениями }
(2)

(since C++20)
( список-выражений )
( список-инициализации )
(3) (until C++11)
(since C++11)
{}
{ список-инициализации }
{ список-инициализации-с-обозначениями }
(4) (since C++11)
(since C++11)
(since C++20)
1) Синтаксис копирующей инициализации.
2) Синтаксис агрегатной инициализации. (до C++11) Синтаксис списковой инициализации. (начиная с C++11)
3) Синтаксис прямой инициализации.
4) Синтаксис списковой инициализации.
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 .
  • Если инициализатор имеет синтаксис (2) :
(до 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 инициализируются как часть запуска потока, до начала выполнения функции потока. Для обоих этих классов переменных инициализация происходит в два отдельных этапа:

Статическая инициализация

Существует две формы статической инициализации:

1) Если возможно, применяется constant initialization .
2) В противном случае, нелокальные статические и thread-local переменные подвергаются zero-инициализации .

На практике:

  • Константная инициализация обычно применяется на этапе компиляции. Предварительно вычисленные представления объектов сохраняются как часть образа программы. Если компилятор этого не делает, он всё равно должен гарантировать, что инициализация произойдёт до любой динамической инициализации.
  • Переменные, подлежащие нулевой инициализации, размещаются в сегменте .bss образа программы, который не занимает места на диске и обнуляется операционной системой при загрузке программы.

Динамическая инициализация

После завершения всей статической инициализации, динамическая инициализация нелокальных переменных происходит в следующих ситуациях:

1) Неупорядоченная динамическая инициализация , которая применяется только к (static/thread-local) статическим членам данных шаблонов классов static data members и variable templates (since C++14) которые не являются explicitly specialized . Инициализация таких статических переменных недетерминированно упорядочена относительно всей другой динамической инициализации за исключением случаев, когда программа запускает поток до инициализации переменной, в этом случае её инициализация неупорядочена (since C++17) . Инициализация таких thread-local переменных неупорядочена относительно всей другой динамической инициализации.
2) Частично-упорядоченная динамическая инициализация , которая применяется ко всем inline-переменным, не являющимся неявно или явно инстанцируемой специализацией. Если частично-упорядоченная V определена до упорядоченной или частично-упорядоченной W в каждой единице трансляции, инициализация V упорядочена до инициализации W (или происходит-до, если программа запускает поток).
(since C++17)
3) Упорядоченная динамическая инициализация , которая применяется ко всем остальным нелокальным переменным: внутри одной единицы трансляции инициализация этих переменных всегда упорядочена в точном соответствии с порядком появления их определений в исходном коде. Инициализация статических переменных в разных единицах трансляции является недетерминированно упорядоченной. Инициализация thread-local переменных в разных единицах трансляции является неупорядоченной.

Если инициализация нелокальной переменной со статической длительностью хранения или длительностью хранения потока завершается через исключение, std::terminate вызывается.

Ранняя динамическая инициализация

Компиляторам разрешено инициализировать динамически инициализируемые переменные как часть статической инициализации (по сути, на этапе компиляции), если выполняются оба следующих условия:

1) динамическая версия инициализации не изменяет значение любого другого объекта области видимости пространства имен до своей инициализации
2) статическая версия инициализации производит то же значение в инициализируемой переменной, которое было бы произведено динамической инициализацией, если бы все переменные, не требующие статической инициализации, были инициализированы динамически.

Из-за приведенного выше правила, если инициализация некоторого объекта 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 было неясно, является ли вычисление аргументов функции
в инициализаторе частью инициализации
это часть инициализации

Смотрите также

Документация C для Инициализации