Spec-Zone.ru › C++

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

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

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

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

class="t-sdsc-begin">
( список-выражений ) (1)
= выражение (2)
{ список-инициализаторов } (3)
{ список-инициализаторов-с-назначением } (4) (с C++20)
1) список выражений, разделённых запятыми, произвольных выражений и инициализаторов в фигурных скобках в скобках
2) знак равенства, за которым следует выражение или инициализатор в фигурных скобках
3) инициализатор в фигурных скобках: возможно пустой, список выражений, разделённых запятыми, и других инициализаторов в фигурных скобках
4) инициализатор в фигурных скобках с инициализаторами с назначением


В зависимости от контекста инициализатор может вызывать:

  • Инициализацию значением, например std::string s{};
  • Прямую инициализацию, например std::string s("hello");
  • Копирующую инициализацию, например std::string s = "hello";
  • Инициализацию списком, например std::string s{'a', 'b', 'c'};
  • Агрегатную инициализацию, например char a[3] = {'a', 'b'};
  • Инициализацию ссылки, например char& c = a[0];

Если инициализатор не предоставлен, применяются правила инициализации по умолчанию.

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

Переменные вне области видимости

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

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

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

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

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

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

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

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

(с 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++.

DR Применяется к Поведение, опубликованное Правильное поведение
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/w/cpp/language/initialization

Spec-Zone.ru

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