Spec-Zone.ru › C++

Инициализация

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

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

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

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

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

(начиная с C++20)
( список-выражений )
( список-инициализаторов )
(3) (до C++11)
(начиная с C++11)
{}
{ список-инициализаторов }
{ список-обозначенных-инициализаторов }
(4) (начиная с C++11)
(начиная с C++11)
(начиная с C++20)
1) Синтаксис копирующей инициализации.
2) Синтаксис агрегатной инициализации.(до C++11)Синтаксис списковой инициализации.(начиная с C++11)
3) Синтаксис прямой инициализации.
4) Синтаксис списковой инициализации.
выражение - любое выражение (кроме выражений с оператором-запятой без скобок)
список-выражений - список выражений, разделённых запятыми (кроме выражений с оператором-запятой без скобок)
список-инициализаторов - список разделённых запятыми предложений инициализации (см. ниже)
список-обозначенных-инициализаторов - список разделённых запятыми обозначенных предложений инициализации


Предложение инициализации может быть одним из следующих:

выражение (1)
{} (2)
{ список-инициализаторов } (3)
{ список-обозначенных-инициализаторов } (4) (начиная с C++20)

Синтаксисы (2-4) в совокупности называются списком инициализаторов в фигурных скобках.

Семантика инициализаторов

Если для объекта не указан инициализатор, объект инициализируется по умолчанию. Если инициализатор не указан для ссылки, программа некорректна.

Если для объекта указан инициализатор () (он не может встречаться в деклараторах из-за ограничения синтаксиса), объект инициализируется значением. Если для ссылки указан инициализатор (), программа некорректна.

Семантика инициализаторов такова:

  • Если инициализируемая сущность является ссылкой, см. инициализацию ссылки.
  • В противном случае инициализируемая сущность является объектом. Пусть тип объекта обозначен как T:
    • Если инициализатор имеет синтаксис (1), объект инициализируется копированием.
  • Если инициализатор имеет синтаксис (2):
    • Если T является агрегатом, применяется агрегатная инициализация.
    • Если T является скалярным типом, T x = { a }; эквивалентно T x = a;.
    • В противном случае программа некорректна.
(до C++11)
  • Если инициализатор имеет синтаксис (2) или (4), объект инициализируется списком.
(начиная с C++11)
  • Если инициализатор имеет синтаксис (3), объект инициализируется напрямую.
#include <string>

std::string s1;           // default-initialization
std::string s2();         // NOT an initialization!
                          // actually declares a function “s2”
                          // with no parameter and returns std::string
std::string s3 = "hello"; // copy-initialization
std::string s4("hello");  // direct-initialization
std::string s5{'a'};      // list-initialization (since C++11)

char a[3] = {'a', 'b'}; // aggregate initialization
                        // (part of list initialization since C++11)
char& c = a[0];         // reference initialization

Нелокальные переменные

Все нелокальные переменные со статической длительностью хранения инициализируются при запуске программы до начала выполнения функции main (если инициализация не отложена; см. ниже). Все нелокальные переменные с потоковой длительностью хранения инициализируются при запуске потока до начала выполнения функции потока. Для обоих классов переменных инициализация происходит в два отдельных этапа:

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

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

1) Если возможно, выполняется константная инициализация.
2) В противном случае нелокальные статические переменные и переменные с потоковой длительностью хранения инициализируются нулём.

На практике:

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

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

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

1) Неупорядоченная динамическая инициализация, применяемая только к (статическим/потоковым) статическим членам данных шаблонов классов и шаблонам переменных(начиная с C++14), которые не являются явно специализированными. Инициализация таких статических переменных неопределённо упорядочена относительно всей остальной динамической инициализации за исключением случая, когда программа запускает поток до инициализации переменной: тогда её инициализация не упорядочена(начиная с C++17). Инициализация таких переменных с потоковой длительностью хранения не упорядочена относительно всей остальной динамической инициализации.
2) Частично упорядоченная динамическая инициализация, применяемая ко всем встроенным переменным, которые не являются неявно или явно инстанцированными специализациями. Если частично упорядоченная переменная V определена перед упорядоченной или частично упорядоченной переменной W во всех единицах трансляции, инициализация V упорядочена перед инициализацией W (или происходит до неё, если программа запускает поток). (начиная с C++17)
3) Упорядоченная динамическая инициализация, применяемая ко всем остальным нелокальным переменным: в пределах одной единицы трансляции инициализация этих переменных всегда упорядочена точно в соответствии с порядком их определений в исходном коде. Инициализация статических переменных в разных единицах трансляции неопределённо упорядочена. Инициализация переменных с потоковой длительностью хранения в разных единицах трансляции не упорядочена.

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

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

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

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

Из-за приведённого выше правила, если инициализация некоторого объекта o1 обращается к объекту o2 с областью видимости пространства имён, который потенциально требует динамической инициализации, но определён позднее в той же единице трансляции, не указано, будет ли использовано значение o2 полностью инициализированного o2 (поскольку компилятор перенёс инициализацию o2 на этап компиляции) или значение o2, лишь инициализированного нулём.

inline double fd() { return 1.0; }

extern double d1;

double d2 = d1;   // unspecified:
                  // dynamically initialized to 0.0 if d1 is dynamically initialized, or
                  // dynamically initialized to 1.0 if d1 is statically initialized, or
                  // statically initialized to 0.0 (because that would be its value
                  // if both variables were dynamically initialized)

double d1 = fd(); // may be initialized statically or dynamically to 1.0

Отложенная динамическая инициализация

Зависит от реализации, происходит ли динамическая инициализация до первого оператора функции main (для статических переменных) или начальной функции потока (для переменных с потоковой длительностью хранения), либо откладывается на более позднее время.

Если инициализация невстроенной переменной(начиная с C++17) отложена до момента после первого оператора функции main/функции потока, она происходит до первого ODR-использования любой переменной со статической/потоковой длительностью хранения, определённой в той же единице трансляции, что и инициализируемая переменная. Если из данной единицы трансляции не выполняется ODR-использование ни одной переменной или функции, определённые в ней нелокальные переменные могут никогда не инициализироваться (это моделирует поведение динамической библиотеки, загружаемой по требованию). Однако, если из единицы трансляции выполняется ODR-использование чего-либо, все определённые в ней нелокальные переменные, инициализация или уничтожение которых имеют побочные эффекты, будут инициализированы, даже если они не используются в программе.

Если инициализация встроенной переменной отложена, она происходит до первого ODR-использования именно этой переменной.

(начиная с C++17)
// ============
// == File 1 ==

#include "a.h"
#include "b.h"

B b;
A::A() { b.Use(); }

// ============
// == File 2 ==

#include "a.h"

A a;

// ============
// == File 3 ==

#include "a.h"
#include "b.h"

extern A a;
extern B b;

int main()
{
    a.Use();
    b.Use();
}

// If a is initialized before main is entered, b may still be uninitialized
// at the point where A::A() uses it (because dynamic initialization is
// indeterminately sequenced across translation units)

// If a is initialized at some point after the first statement of main (which odr-uses
// a function defined in File 1, forcing its dynamic initialization to run),
// then b will be initialized prior to its use in A::A

Локальные статические переменные

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

В объявлении переменной с внешним или внутренним связыванием в области видимости блока инициализатор не допускается. Такое объявление должно содержать extern и не может быть определением.

Члены классов

Нестатические члены данных можно инициализировать с помощью списка инициализаторов членов или инициализатора члена по умолчанию.

Примечания

Порядок уничтожения нелокальных переменных описан в std::exit.

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены задним числом к ранее опубликованным стандартам C++.

Отчёт об ошибке Применяется к Поведение согласно опубликованной версии Правильное поведение
CWG 270 C++98 порядок инициализации статических членов данных
шаблонов классов не был определён
определён как неупорядоченный, кроме случаев
явных специализаций и определений
CWG 441 C++98 нелокальные ссылки со статической длительностью хранения
не всегда инициализировались до динамической инициализации
считаются статической инициализацией и всегда
инициализируются до динамической инициализации
CWG 1415 C++98 объявление переменной extern в области видимости блока
могло быть определением
запрещено (в таких объявлениях
не допускается инициализатор)
CWG 2599 C++98 не было ясно, является ли вычисление аргументов
функции в инициализаторе частью инициализации
является частью инициализации

См. также

  • устранение копирования
  • преобразующий конструктор
  • конструктор копирования
  • конструктор по умолчанию
  • explicit
  • конструктор перемещения
  • new
Документация по C для инициализации

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/initialization

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API