Инициализация значением
Это инициализация выполняется, когда объект создаётся с пустым инициализатором.
Содержание |
[править] Синтаксис
T ()
|
(1) | ||||||||
new T ()
|
(2) | ||||||||
Класс:: Класс( ...) : элемент () { ... }
|
(3) | ||||||||
T объект {};
|
(4) | (начиная с C++11) | |||||||
T {}
|
(5) | (начиная с C++11) | |||||||
new T {}
|
(6) | (начиная с C++11) | |||||||
Класс:: Класс( ...) : элемент {} { ... }
|
(7) | (начиная с C++11) | |||||||
[править] Объяснение
Инициализация значения выполняется в следующих ситуациях:
4) когда именованный объект (автоматический, статический или локальный для потока) объявляется с инициализатором, состоящим из пары фигурных скобок.
|
(начиная с C++11) |
Во всех случаях, если используется пустая пара фигурных скобок {}
и T
является агрегатным типом, вместо инициализации значением выполняется агрегатная инициализация.
Если |
(начиная с C++11) |
Эффекты инициализации значением:
T
является типом класса без конструктора по умолчанию или с объявленным пользователем (до C++11)предоставленным пользователем или удалённым (начиная с C++11) конструктором по умолчанию, объект инициализируется по умолчанию;T
является типом класса с конструктором по умолчанию, который не объявлен пользователем (до C++11)ни предоставлен пользователем, ни удалённый (начиная с C++11) (то есть это может быть класс с неявно определённым или по умолчанию определённым конструктором), объект инициализацируется нулём и проверяются семантические ограничения для инициализации по умолчанию, �� если T
имеет нетривиальный конструктор по умолчанию, объект инициализируется по умолчанию;T
является типом массива, каждый элемент массива инициализируется значением;[править] Примечание
Синтаксис T object(); не инициализирует объект; он объявляет функцию, которая не принимает аргументов и возвращает T
. Способ инициализации значения именованной переменной до C++11 был T object = T();, который инициализирует значением временный объект, а затем инициализирует копированием объект: большинство компиляторов оптимизирует копирование в этом случае.
Ссылки не могут быть инициализированы значением.
Как описано в разделе функциональное приведение, синтаксис T() (1) запрещён для массивов, а T{} (5) разрешён.
Все стандартные контейнеры (std::vector, std::list и т.д.) инициализируют свои элементы значением при создании с одним аргументом size_type
или при расширении вызовом resize(), если только их распределитель не настраивает поведение конструкции.
Стандарт указывает, что нулевая инициализация не выполняется, когда класс имеет предоставленный пользователем или удалённый конструктор по умолчанию, что означает, что не учитывается, выбран ли указанный конструктор по умолчанию разрешением перегрузки. Все известные компиляторы выполняют дополнительную инициализацию нулями, если выбран неудаляемый конструктор по умолчанию.
struct A { A() = default; template<class = void> A(int = 0) {} // A имеет предоставленный пользователем конструктор по умолчанию, // который не выбран int x; }; constexpr int test(A a) { return a.x; // поведение не определено, если значение a не определено } constexpr int zero = test(A()); // некорректно: параметр не инициализируется нулём в соответствии со стандартом, // что приводит к неопределённому поведению, которое делает программу некорректной // в контекстах, где требуется константная оценка. // Однако такой код принимается всеми известными компиляторами. void f() { A a = A(); // не инициализируется нулём по стандарту // но реализации тем не менее генерируют код для инициализации нулём }
[править] Пример
#include <cassert> #include <iostream> #include <string> #include <vector> struct T1 { int mem1; std::string mem2; virtual void foo() {} // убедитесь, что T1 не является агрегатом }; // неявный конструктор по умолчанию struct T2 { int mem1; std::string mem2; T2(const T2&) {} // предоставленный пользователем конструктор копирования }; // нет конструктора по умолчанию struct T3 { int mem1; std::string mem2; T3() {} // предоставленный пользователем конструктор по умолчанию }; std::string s{}; // инициализация по умолчанию, значение равно "" int main() { int n{}; // scalar => инициализация нулём, значение равно 0 assert(n == 0); double f = double(); // scalar => инициализация нулём, значение равно 0.0 assert(f == 0.0); int* a = new int[10](); // array => инициализация значением каждого элемента assert(a[9] == 0); // значение каждого элемента равно 0 T1 t1{}; // класс с неявным конструктором по умолчанию => assert(t1.mem1 == 0); // t1.mem1 инициализируется нулём, значение равно 0 assert(t1.mem2 == ""); // t1.mem2 инициализируется по умолчанию, значение равно "" // T2 t2{}; // ошибка: класс без конструктора по умолчанию T3 t3{}; // класс с предоставленным пользователем конструктором // по умолчанию => std::cout << t3.mem1; // t3.mem1 инициализируется по умолчанию неопределённым // значением assert(t3.mem2 == ""); // t3.mem2 инициализируется по умолчанию, значение равно "" std::vector<int> v(3); // инициализация значения каждого элемента assert(v[2] == 0); // значение каждого элемента равно 0 std::cout << '\n'; delete[] a; }
Возможный вывод:
42
[править] Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
CWG 178 | C++98 | не было инициализации значением; пустой инициализатор вызывает нициализацию по умолчанию (хотя new T() также выполняет инициализацию нулём) |
пустой инициализатор вызывает инициализацию значением |
CWG 543 | C++98 | инициализация значением для объекта класса без каких-либо конструкторов, предоставленных пользователем, была эквивалентна инициализации значением каждого подобъекта (что не требует инициализации нулём элемента с предоставленным пользователем конструктором по умолчанию) |
инициализирует нулём весь объект, затем вызывает конструктор по умолчанию |
CWG 1301 | C++11 | инициализация значений объединений с удалёнными конструкторами по умолчанию приводит к инициализации нулём |
они инициализируются по умолчанию |
CWG 1368 | C++98 | любой предоставленный пользователем конструктор вызывает пропуск инициализации нулём |
только предоставленный пользователем конструктор по умолчанию пропускает инициализацию нулём |
CWG 1502 | C++11 | инициализация объединения значением без конструктора по умолчанию, предоставленного пользователем, инициализировала объект только нулём, несмотря на инициализаторы элементов по умолчанию |
выполняет инициализацию по умолчанию после инициализации нулём |
CWG 1507 | C++98 | инициализация значением для объекта класса без каких-либо пользовательских конструкторов не проверяла действительность конструктора по умолчанию, когда последний тривиален |
проверяется допустимость тривиального конструктора по умолчанию |