Инициализация по умолчанию
Это инициализация выполняется, когда объект создается без инициализатора.
Синтаксис
T объект ;
|
(1) | ||||||||
new T
|
(2) | ||||||||
Объяснение
Инициализация по умолчанию выполняется в трёх случаях:
Эффект инициализации по умолчанию:
- если
Tявляется (возможно cv-квалифицированным) не-POD (до C++11) типом класса, конструкторы рассматриваются и подвергаются разрешению перегрузки с пустым списком аргументов. Выбранный конструктор (который является одним из конструкторов по умолчанию) вызывается для предоставления начального значения для нового объекта; - если
Tявляется типом массива, каждый элемент массива инициализируется по умолчанию; - иначе инициализация не производится (смотрите примечание).
Инициализация по умолчанию константного объекта
Если программа требует инициализации по умолчанию объекта const-квалифицированного типа T, T должен быть константно по умолчанию создаваемым типом класса или его массивом.
Тип класса T является константно по умолчанию создаваемым, если инициализация по умолчанию T вызовет предоставленный пользователем конструктор класса T (не унаследованный от базового класса) (начиная с C++11) или если
|
Только (возможно, cv-квалифицированные) не-POD типы классов (или их массивы) с автоматической длительностью хранения считались инициализированными по умолчанию, когда не используется инициализатор. Скалярные и POD типы с динамической длительностью хранения считались неинициализированными (начиная с C++11, эта ситуация была переклассифицирована как форма инициализации по умолчанию). |
(до C++11) |
|
(до C++11) |
|
(начиная с C++11) |
каждый потенциально конструируемый базовый класс T является константно по умолчанию создаваемым.
Чтение из неопределённого байта
Использование неопределённого значения, полученного путём инициализации по умолчанию переменной любого типа, не относящейся к классу, является неопределённым поведением (в частности, это может быть представлением ловушкой), за исключением следующих случаев:
- если неопределённое значение типа
unsigned charили std::byte (начиная с C++17) присвоено другой (возможно, cv-квалифицированной) переменной типаunsigned charили std::byte (начиная с C++17) (значение переменной становится неопределённым, но поведение нет); - если неопределённое значение типа
unsigned charили std::byte (начиная с C++17) используется для инициализации другой (возможно, cv-квалифицированной) переменной типаunsigned charили std::byte (начиная с C++17); - если неопределённое значение типа
unsigned charили std::byte (начиная с C++17) является результатом
- второго или третьего операнда условного выражения,
- правым операндом оператора запятая,
- операнда приведения или преобразования в (возможно, cv-квалифицированный)
unsigned charили std::byte (начиная с C++17), - выражения с отброшенным значением.
int f(bool b)
{
int x; // OK: значение x неопределено
int y = x; // неопределённое поведение
unsigned char c; // OK: значение c неопределено
unsigned char d = c; // OK: значение d неопределено
int e = d; // неопределённое поведение
return b ? d : 0; // поведение неопределено, если b истинно
}
Примечание
Инициализация по умолчанию переменных, не относящихся к классу, с автоматической и динамической длительностью хранения, создаёт объекты с неопределёнными значениями (статические и локальные объекты потока получают инициализацию нулём).
Ссылочные и константные скалярные объекты не могут быть инициализированы по умолчанию.
Пример
#include <string>
struct T1 { int mem; };
struct T2
{
int mem;
T2() { } // "mem" отсутствует в списке инициализаторов
};
int n; // статическая неклассовая переменная, выполняется двухэтапная инициализация:
// 1) нулевая инициализация инициализирует n нулём
// 2) инициализация по умолчанию ничего не делает, оставляя n равным нулю
int main()
{
int n; // не класс, значение неопределено
std::string s; // класс, вызывает конструктор по умолчанию, значение ""
// (пустая строка)
std::string a[2]; // массив, инициализирует элементы по умолчанию, значение
// равно {"", ""}
// int& r; // ошибка: ссылка
// const int n; // ошибка: константа не класс
// const T1 t1; // ошибка: константный класс с неявным конструктором по умолчанию
T1 t1; // класс, вызывает неявный конструктор по умолчанию
const T2 t2; // константный класс, вызывает предоставленный пользователем
// конструктором по умолчанию
// t2.mem инициализируется по умолчанию (в неопределённое значение)
}
Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| CWG 178 | C++98 | нет инициализации значением; пустой инициализатор вызывает инициализацию по умолчанию (хотя new T() также выполняетинициализацию нулём) |
пустой инициализатор вызывает инициализацию значением |
| CWG 253 | C++98 | инициализация константного объекта по умолчанию не могла вызвать неявно объявленный конструктор по умолчанию |
разрешено, если все подобъекты инициализированы |
| CWG 616 | C++98 | преобразование lvalue в rvalue любого неинициализированного объекта всегда было неопределённым поведением |
неопределённый unsigned char разрешён
|
| CWG 1787 | C++98 | чтение из неопределённого unsigned char, кэшированного врегистре, было неопределённым поведением |
сделано чётко определённым |