Инициализация
Инициализация переменной предоставляет её начальное значение во время создания.
Начальное значение может быть предоставлено в разделе инициализатора декларатора или в выражении new. Оно также происходит во время вызовов функций: параметры функций и значения возвращаемых функций также инициализируются.
Для каждого декларатора инициализатор может быть одним из следующих:
( список-выражений ) | (1) | |
= выражение | (2) | |
{ список-инициализаторов } | (3) | |
{ список-инициализаторов-с-назначением } | (4) | (с C++20) |
В зависимости от контекста инициализатор может вызывать:
- Инициализацию значением, например
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];
Если инициализатор не предоставлен, применяются правила инициализации по умолчанию.
Инициализация включает вычисление всех подвыражений внутри инициализатора и создание любых временных объектов для аргументов функций или возвращаемых значений.
Переменные вне области видимости
Все переменные вне области видимости со статическим временем жизни инициализируются как часть запуска программы, до начала выполнения главной функции (если не отложено, см. ниже). Все переменные вне области видимости с локальным для потока временем жизни инициализируются во время запуска потока, до начала выполнения функции потока. Для обеих этих категорий переменных инициализация происходит в двух отдельных этапах:
Статическая инициализация
Существуют две формы статической инициализации:
На практике:
- Инициализация константой обычно применяется во время компиляции. Предвычисленные представления объектов хранятся как часть образа программы. Если компилятор этого не делает, он всё равно должен гарантировать, что инициализация происходит до любой динамической инициализации.
- Переменные, подлежащие инициализации нулями, размещаются в сегменте
.bssобраза программы, который не занимает места на диске и обнуляется операционной системой при загрузке программы.
Динамическая инициализация
После завершения всей статической инициализации происходит динамическая инициализация переменных вне области видимости в следующих ситуациях:
| 2) Частично-упорядоченная динамическая инициализация, которая применяется ко всем встроенным переменным, которые не являются явным или неявным специализированным экземпляром. Если частично-упорядоченная V определена до упорядоченной или частично-упорядоченной W во всех единицах трансляции, инициализация V упорядочена перед инициализацией W (или происходит раньше, если программа запускает поток). | (с C++17) |
Если инициализация переменной вне области видимости со статическим или локальным для потока временем жизни завершается исключением, вызывается std::terminate.
Ранняя динамическая инициализация
Компиляторы могут инициализировать динамически инициализируемые переменные как часть статической инициализации (по сути, во время компиляции), если оба следующих условия верны:
Из-за правила выше, если инициализация некоторого объекта 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