Я очень запутался в инициализации значений, значений по умолчанию и нуля. и особенно когда они вступают в силу различных стандартов C ++ 03 и C ++ 11 (и C ++ 14 ).
Я цитирую и пытаюсь расширить действительно хороший ответ Value- / Default- / Zero- Init C ++ 98 и C ++ 03 здесь, чтобы сделать его более общим, поскольку это помогло бы многим пользователям, если бы кто-то мог помочь заполнить нужны пробелы, чтобы иметь хорошее представление о том, что и когда происходит?
Полный анализ примеров вкратце:
Иногда память, возвращаемая оператором new, будет инициализирована, а иногда это не будет зависеть от того, является ли тип, который вы обновляете, POD (простые старые данные) , или если это класс, который содержит члены POD и использует конструктор по умолчанию, созданный компилятором.
- В C ++ 1998 есть 2 типа инициализации: инициализация нуля и инициализация по умолчанию.
- В C ++ 2003 был добавлен 3-й тип инициализации - инициализация значения .
- В C ++ 2011 / C ++ 2014 была добавлена только инициализация списка, а правила для инициализации значения / значения по умолчанию / нуля немного изменены.
Предполагать:
struct A { int m; };
struct B { ~B(); int m; };
struct C { C() : m(){}; ~C(); int m; };
struct D { D(){}; int m; };
struct E { E() = default; int m;}; /** only possible in c++11/14 */
struct F {F(); int m;}; F::F() = default; /** only possible in c++11/14 */
В компиляторе C ++ 98 должно произойти следующее :
new A
- неопределенное значение (A
POD)new A()
- нулевая инициализацияnew B
- конструкция по умолчанию (B::m
не инициализирована, неB
является POD)new B()
- конструкция по умолчанию (B::m
не инициализирована)new C
- конструкция по умолчанию (C::m
инициализирована нулем, неC
является POD)new C()
- конструкция по умолчанию (C::m
инициализируется нулем)new D
- конструкция по умолчанию (D::m
не инициализирована, неD
является POD)new D()
- конструкция по умолчанию? (D::m
не инициализирован)
В компиляторе, совместимом с C ++ 03, все должно работать так:
new A
- неопределенное значение (A
POD)new A()
- value-initializeA
, что является нулевой инициализацией, поскольку это POD.new B
- инициализируется по умолчанию (неB::m
инициализируется, неB
является POD)new B()
- value-initializes,B
который инициализирует нулем все поля, поскольку его ctor по умолчанию создается компилятором, а не определяемым пользователем.new C
- default-initializesC
, который вызывает ctor по умолчанию. (C::m
инициализируется нулем, неC
является POD)new C()
- value-initializesC
, который вызывает ctor по умолчанию. (C::m
инициализируется нулем)new D
- конструкция по умолчанию (D::m
не инициализирована, неD
является POD)new D()
- инициализирует значение D? , который вызывает ctor по умолчанию (D::m
не инициализирован)
Значения курсивом и? есть сомнения, пожалуйста, помогите исправить это :-)
В компиляторе, совместимом с C ++ 11, все должно работать так:
??? (пожалуйста, помогите, если я начну здесь, все равно пойдет не так)
В компиляторе, совместимом с C ++ 14, все должно работать так: ??? (пожалуйста, помогите, если я начну здесь, все равно пойдет не так) (Черновик на основе ответа)
new A
- инициализируется по умолчаниюA
, компилятор gen. ctor, (оставляетA::m
неинициализированным) (A
POD)new A()
- value-initializesA
, что является нулевой инициализацией с момента 2. точки в [dcl.init] / 8new B
- инициализируется по умолчаниюB
, компилятор gen. ctor, (оставляетB::m
неинициализированным) (неB
является POD)new B()
- value-initializes,B
который инициализирует нулем все поля, поскольку его ctor по умолчанию создается компилятором, а не определяемым пользователем.new C
- default-initializesC
, который вызывает ctor по умолчанию. (C::m
инициализируется нулем, неC
является POD)new C()
- value-initializesC
, который вызывает ctor по умолчанию. (C::m
инициализируется нулем)new D
- инициализируется по умолчаниюD
(D::m
не инициализируется, неD
является POD)new D()
- value-initializesD
, который вызывает ctor по умолчанию (D::m
не инициализирован)new E
- default-initializesE
, который вызывает comp. ген. ctor. (E::m
не инициализирован, E не POD)new E()
- значение-инициализируетE
, который инициализируется нулем,E
начиная с 2 точки в [dcl.init] / 8 )new F
- default-initializesF
, который вызывает comp. ген. ctor. (F::m
не инициализирован, неF
является POD)new F()
- инициализирует значениеF
, которое инициализируется по умолчанию,F
начиная с 1. точки в [dcl.init] / 8 (F
функция ctor предоставляется пользователем, если она объявлена пользователем и не используется явно по умолчанию или не удалена при первом объявлении. Ссылка )
struct D { D() {}; int m; };
возможно, стоит включить в свой список.Ответы:
C ++ 14 определяет инициализацию объектов, созданных с помощью
new
[expr.new] / 17 ([expr.new] / 15 в C ++ 11, и тогда примечание было не примечанием, а нормативным текстом):Инициализация по умолчанию определяется в [dcl.init] / 7 (/ 6 в C ++ 11, и сама формулировка имеет тот же эффект):
Таким образом
new A
вызывает толькоA
конструктор по умолчанию, который не инициализируетсяm
. Неопределенное значение. Должно быть то же самое дляnew B
.new A()
интерпретируется согласно [dcl.init] / 11 (/ 10 в C ++ 11):А теперь рассмотрим [dcl.init] / 8 (/ 7 в C ++ 11 †):
Следовательно,
new A()
будет инициализироваться нулемm
. И это должно быть эквивалентно дляA
иB
.new C
иnew C()
снова инициализирует объект по умолчанию, поскольку применяется первая точка маркера из последней цитаты (C имеет конструктор по умолчанию, предоставленный пользователем!). Но, очевидно,m
в обоих случаях в конструкторе инициализируется now .† Что ж, этот абзац имеет несколько другую формулировку в C ++ 11, что не влияет на результат:
источник
struct A { int m; }; struct C { C() : m(){}; int m; };
получаются разные результаты и что вызывает инициализацию m в A в первую очередь. Я открыл специальную ветку для эксперимента, который я провел, и буду признателен за ваш вклад, чтобы прояснить проблему. Спасибо stackoverflow.com/questions/45290121/…Следующий ответ расширяет ответ https://stackoverflow.com/a/620402/977038, который будет служить ссылкой для C ++ 98 и C ++ 03.
Цитируя ответ
C ++ 11 (применительно к n3242)
Инициализаторы
8.5 Инициализаторы [dcl.init] указывает, что переменная POD или не POD может быть инициализирована либо как фигурная скобка-или-равный-инициализатор, который может быть либо фигурным-инициализирующим списком, либо предложением инициализатора, совокупно именуемым фигурной-или-равным- инициализатор или с помощью (список-выражений) . До C ++ 11 поддерживалось только (список-выражений) или предложение- инициализатор, хотя предложение- инициализатор было более ограниченным, чем то, что есть в С ++ 11. В C ++ 11 предложение- инициализатор теперь поддерживает список-инициализации в фигурных скобках, кроме выражения-присваивания.как было в C ++ 03. Следующая грамматика резюмирует новое поддерживаемое предложение, в котором часть, выделенная жирным шрифтом, была недавно добавлена в стандарт C ++ 11.
инициализатор:
скобка или равно-инициализатор
(выражение-лист)
бандаж или равно-инициализатор:
= инициализатор-раздел
приготовился-инициализировать-лист
инициализатор-п:
назначение выражение
приготовилось-INIT-лист
инициализатор-списка:
инициализатор-раздел ... opt
список-инициализаторов, предложение-инициализатора ... opt **
braced-init-list:
{список-инициализаторов, opt}
{}
Инициализация
Как и C ++ 03, C ++ 11 по-прежнему поддерживает три формы инициализации
Заметка
Тип инициализатора: 8.5.5 [dcl.init] _zero-initialize_
Выполняется в следующих случаях
2. Тип инициализатора: 8.5.6 [dcl.init] _default-initialize_
Выполняется в следующих случаях
3. Тип инициализатора: 8.5.7 [dcl.init] _value-initialize_
Итак, чтобы подвести итог
источник
Я могу подтвердить, что в C ++ 11 все, что упомянуто в вопросе в C ++ 14, является правильным, по крайней мере, в соответствии с реализациями компилятора.
Чтобы проверить это, я добавил в свой набор тестов следующий код . Я тестировал
-std=c++11 -O3
в GCC 7.4.0, GCC 5.4.0, Clang 10.0.1 и VS 2017, и все тесты ниже прошли успешно.#include <gtest/gtest.h> #include <memory> struct A { int m; }; struct B { int m; ~B(){}; }; struct C { int m; C():m(){}; ~C(){}; }; struct D { int m; D(){}; }; struct E { int m; E() = default; }; struct F { int m; F(); }; F::F() = default; // We use this macro to fill stack memory with something else than 0. // Subsequent calls to EXPECT_NE(a.m, 0) are undefined behavior in theory, but // pass in practice, and help illustrate that `a.m` is indeed not initialized // to zero. Note that we initially tried the more aggressive test // EXPECT_EQ(a.m, 42), but it didn't pass on all compilers (a.m wasn't equal to // 42, but was still equal to some garbage value, not zero). // #define FILL { int m = 42; EXPECT_EQ(m, 42); } // We use this macro to fill heap memory with something else than 0, before // doing a placement new at that same exact location. Subsequent calls to // EXPECT_EQ(a->m, 42) are undefined behavior in theory, but pass in practice, // and help illustrate that `a->m` is indeed not initialized to zero. // #define FILLH(b) std::unique_ptr<int> bp(new int(42)); int* b = bp.get(); EXPECT_EQ(*b, 42) TEST(TestZero, StackDefaultInitialization) { { FILL; A a; EXPECT_NE(a.m, 0); } // UB! { FILL; B a; EXPECT_NE(a.m, 0); } // UB! { FILL; C a; EXPECT_EQ(a.m, 0); } { FILL; D a; EXPECT_NE(a.m, 0); } // UB! { FILL; E a; EXPECT_NE(a.m, 0); } // UB! { FILL; F a; EXPECT_NE(a.m, 0); } // UB! } TEST(TestZero, StackValueInitialization) { { FILL; A a = A(); EXPECT_EQ(a.m, 0); } { FILL; B a = B(); EXPECT_EQ(a.m, 0); } { FILL; C a = C(); EXPECT_EQ(a.m, 0); } { FILL; D a = D(); EXPECT_NE(a.m, 0); } // UB! { FILL; E a = E(); EXPECT_EQ(a.m, 0); } { FILL; F a = F(); EXPECT_NE(a.m, 0); } // UB! } TEST(TestZero, StackListInitialization) { { FILL; A a{}; EXPECT_EQ(a.m, 0); } { FILL; B a{}; EXPECT_EQ(a.m, 0); } { FILL; C a{}; EXPECT_EQ(a.m, 0); } { FILL; D a{}; EXPECT_NE(a.m, 0); } // UB! { FILL; E a{}; EXPECT_EQ(a.m, 0); } { FILL; F a{}; EXPECT_NE(a.m, 0); } // UB! } TEST(TestZero, HeapDefaultInitialization) { { FILLH(b); A* a = new (b) A; EXPECT_EQ(a->m, 42); } // ~UB { FILLH(b); B* a = new (b) B; EXPECT_EQ(a->m, 42); } // ~UB { FILLH(b); C* a = new (b) C; EXPECT_EQ(a->m, 0); } { FILLH(b); D* a = new (b) D; EXPECT_EQ(a->m, 42); } // ~UB { FILLH(b); E* a = new (b) E; EXPECT_EQ(a->m, 42); } // ~UB { FILLH(b); F* a = new (b) F; EXPECT_EQ(a->m, 42); } // ~UB } TEST(TestZero, HeapValueInitialization) { { FILLH(b); A* a = new (b) A(); EXPECT_EQ(a->m, 0); } { FILLH(b); B* a = new (b) B(); EXPECT_EQ(a->m, 0); } { FILLH(b); C* a = new (b) C(); EXPECT_EQ(a->m, 0); } { FILLH(b); D* a = new (b) D(); EXPECT_EQ(a->m, 42); } // ~UB { FILLH(b); E* a = new (b) E(); EXPECT_EQ(a->m, 0); } { FILLH(b); F* a = new (b) F(); EXPECT_EQ(a->m, 42); } // ~UB } TEST(TestZero, HeapListInitialization) { { FILLH(b); A* a = new (b) A{}; EXPECT_EQ(a->m, 0); } { FILLH(b); B* a = new (b) B{}; EXPECT_EQ(a->m, 0); } { FILLH(b); C* a = new (b) C{}; EXPECT_EQ(a->m, 0); } { FILLH(b); D* a = new (b) D{}; EXPECT_EQ(a->m, 42); } // ~UB { FILLH(b); E* a = new (b) E{}; EXPECT_EQ(a->m, 0); } { FILLH(b); F* a = new (b) F{}; EXPECT_EQ(a->m, 42); } // ~UB } int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }
Места, где
UB!
упоминается, относятся к неопределенному поведению, а фактическое поведение, вероятно, будет зависеть от многих факторов (a.m
может быть равно 42, 0 или какой-то другой мусор). Места, где~UB
упоминается, также являются неопределенным поведением в теории, но на практике, из-за использования нового размещения, очень маловероятно,a->m
что будет равно чему-либо другому, кроме 42.источник