Saya sangat bingung tentang nilai- & default- & nol-inisialisasi. dan terutama ketika mereka menendang untuk standar yang berbeda C ++ 03 dan C ++ 11 (dan C ++ 14 ).
Saya mengutip dan mencoba untuk memperluas jawaban yang sangat bagus Value- / Default- / Zero- Init C ++ 98 dan C ++ 03 di sini untuk membuatnya lebih umum karena akan membantu banyak pengguna jika seseorang dapat membantu mengisi celah yang dibutuhkan untuk mendapatkan gambaran yang baik tentang apa yang terjadi dan kapan?
Wawasan lengkap dengan contoh singkatnya:
Terkadang memori yang dikembalikan oleh operator baru akan diinisialisasi, dan terkadang tidak tergantung pada apakah jenis yang Anda perbarui adalah POD (data lama biasa) , atau jika itu adalah kelas yang berisi anggota POD dan menggunakan compiler-generated default constructor.
- Di C ++ 1998 ada 2 jenis inisialisasi: nol- dan default-inisialisasi
- Dalam C ++ 2003 tipe ketiga inisialisasi, nilai-inisialisasi ditambahkan.
- Di C ++ 2011 / C ++ 2014 hanya inisialisasi daftar yang ditambahkan dan aturan untuk nilai- / default- / zero-inisialisasi berubah sedikit.
Menganggap:
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 */
Dalam kompiler C ++ 98, hal berikut harus terjadi :
new A
- nilai tak tentu (A
adalah POD)new A()
- nol-menginisialisasinew B
- konstruksi default (B::m
tidak diinisialisasi,B
bukan POD)new B()
- konstruksi default (B::m
tidak diinisialisasi)new C
- konstruksi default (C::m
diinisialisasi nol,C
non-POD)new C()
- konstruksi default (C::m
diinisialisasi nol)new D
- konstruksi default (D::m
tidak diinisialisasi,D
bukan POD)new D()
- konstruksi default? (D::m
tidak dimulai)
Dalam kompilator konforman C ++ 03, hal-hal akan bekerja seperti ini:
new A
- nilai tak tentu (A
adalah POD)new A()
- value-initializeA
, yang merupakan inisialisasi nol karena ini adalah POD.new B
- default-inisialisasi (B::m
tidak diinisialisasi,B
non-POD)new B()
- value-initializesB
yang menginisialisasi nol semua bidang karena ctor defaultnya adalah kompilator yang dihasilkan sebagai lawan yang ditentukan pengguna.new C
- default-inisialisasiC
, yang memanggil ctor default. (C::m
tidak diinisialisasi,C
bukan POD)new C()
- nilai-menginisialisasiC
, yang memanggil ctor default. (C::m
diinisialisasi nol)new D
- konstruksi default (D::m
tidak diinisialisasi,D
bukan POD)new D()
- nilai-menginisialisasi D? , yang memanggil ctor default (D::m
tidak diinisialisasi)
Nilai miring dan? adalah ketidakpastian, tolong bantu untuk memperbaikinya :-)
Dalam compiler konforman C ++ 11, semuanya akan bekerja seperti ini:
??? (tolong bantu jika saya mulai di sini tetap saja akan salah)
Dalam kompilator konforman C ++ 14, hal-hal harus bekerja seperti ini: ??? (tolong bantu jika saya mulai di sini tetap saja akan salah) (Draf berdasarkan jawaban)
new A
- default-inisialisasiA
, compiler gen. ctor, (leavsA::m
uninitialized) (A
adalah POD)new A()
- nilai-menginisialisasiA
, yang merupakan inisialisasi nol sejak 2. titik di [dcl.init] / 8new B
- default-inisialisasiB
, kompilator gen. ctor, (leavsB::m
uninitialized) (B
non-POD)new B()
- value-initializesB
yang menginisialisasi nol semua bidang karena ctor defaultnya adalah kompilator yang dihasilkan sebagai lawan yang ditentukan pengguna.new C
- default-inisialisasiC
, yang memanggil ctor default. (C::m
tidak diinisialisasi,C
bukan POD)new C()
- nilai-menginisialisasiC
, yang memanggil ctor default. (C::m
diinisialisasi nol)new D
- default-inisialisasiD
(D::m
tidak diinisialisasi,D
bukan POD)new D()
- value-initializesD
, yang memanggil ctor default (D::m
tidak diinisialisasi)new E
- default-inisialisasiE
, yang memanggil comp. gen. ctor. (E::m
tidak diinisialisasi, E bukan POD)new E()
- nilai-menginisialisasiE
, yang menginisialisasi nolE
sejak 2 titik di [dcl.init] / 8 )new F
- default-inisialisasiF
, yang memanggil comp. gen. ctor. (F::m
tidak diinisialisasi,F
bukan POD)new F()
- nilai-menginisialisasiF
, yang menginisialisasi defaultF
sejak 1. titik di [dcl.init] / 8 (F
fungsi ctor disediakan pengguna jika dideklarasikan oleh pengguna dan tidak secara eksplisit default atau dihapus pada deklarasi pertamanya. Tautan )
struct D { D() {}; int m; };
mungkin ada baiknya untuk dimasukkan dalam daftar Anda.Jawaban:
C ++ 14 menentukan inisialisasi objek yang dibuat dengan
new
di [expr.new] / 17 ([expr.new] / 15 di C ++ 11, dan catatan tersebut bukanlah catatan tetapi teks normatif saat itu):Default-inisialisasi didefinisikan di [dcl.init] / 7 (/ 6 di C ++ 11, dan susunan kata itu sendiri memiliki efek yang sama):
Jadi
new A
hanya menyebabkanA
konstruktor default dipanggil, yang tidak diinisialisasim
. Nilai tak tentu. Harus sama untuknew B
.new A()
diinterpretasikan menurut [dcl.init] / 11 (/ 10 dalam C ++ 11):Dan sekarang pertimbangkan [dcl.init] / 8 (/ 7 di C ++ 11 †):
Karenanya
new A()
akan menginisialisasi nolm
. Dan ini harus setara denganA
danB
.new C
dannew C()
akan menginisialisasi objek secara default lagi, karena poin pertama dari kutipan terakhir berlaku (C memiliki konstruktor default yang disediakan pengguna!). Tapi, jelas, sekarangm
diinisialisasi di konstruktor dalam kedua kasus.† Nah, paragraf ini memiliki kata-kata yang sedikit berbeda di C ++ 11, yang tidak mengubah hasil:
sumber
struct A { int m; }; struct C { C() : m(){}; int m; };
menghasilkan hasil yang berbeda dan apa yang menyebabkan m di A diinisialisasi di tempat pertama. Saya telah membuka utas khusus untuk percobaan yang saya lakukan dan saya akan menghargai masukan Anda di sana untuk memperjelas masalah. Terima kasih stackoverflow.com/questions/45290121/…Jawaban berikut memperluas jawaban https://stackoverflow.com/a/620402/977038 yang akan berfungsi sebagai referensi untuk C ++ 98 dan C ++ 03
Mengutip jawabannya
C ++ 11 (Mengacu pada n3242)
Penginisialisasi
8.5 Penginisialisasi [dcl.init] menentukan bahwa variabel POD atau non POD dapat diinisialisasi baik sebagai brace-or-equal-initializer yang dapat berupa braced-init-list atau initializer-clause yang secara agregat disebut sebagai brace-or-equal- penginisialisasi atau menggunakan (daftar ekspresi) . Sebelum C ++ 11, hanya (daftar ekspresi) atau klausa penginisialisasi yang didukung meskipun klausa penginisialisasi lebih dibatasi daripada yang kita miliki di C ++ 11. Di C ++ 11, initializer-clause sekarang mendukung braced-init-list selain dari assignment-expressionseperti di C ++ 03. Tata bahasa berikut meringkas klausa baru yang didukung, di mana bagian yang dicetak tebal baru ditambahkan dalam standar C ++ 11.
penginisialisasi:
brace-or-equal-initializer
(daftar ekspresi)
brace-or-equal-initializer:
= penginisialisasi-klausa
braced-init-list
penginisialisasi-klausa:
penugasan-ekspresi
braced-init-list
penginisialisasi-daftar:
penginisialisasi-klausa ...
pilih daftar penginisialisasi, klausa penginisialisasi ... pilih **
braced-init-list:
{daftar penginisialisasi, pilih}
{}
Inisialisasi
Seperti C ++ 03, C ++ 11 masih mendukung tiga bentuk inisialisasi
Catatan
Jenis Penginisialisasi: 8.5.5 [dcl.init] _zero-initialize_
Dilakukan dalam kasus berikut
2. Jenis Penginisialisasi: 8.5.6 [dcl.init] _default-initialize_
Dilakukan dalam kasus berikut
3. Jenis Penginisialisasi: 8.5.7 [dcl.init] _value-initialize_
Jadi untuk meringkas
sumber
Saya dapat mengonfirmasi bahwa di C ++ 11, semua yang disebutkan dalam pertanyaan di bawah C ++ 14 sudah benar, setidaknya sesuai implementasi kompiler.
Untuk memverifikasi ini, saya menambahkan kode berikut ke rangkaian pengujian saya . Saya menguji dengan
-std=c++11 -O3
GCC 7.4.0, GCC 5.4.0, Clang 10.0.1, dan VS 2017, dan semua pengujian di bawah ini berhasil.#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(); }
Tempat-tempat
UB!
yang disebutkan adalah perilaku yang tidak ditentukan, dan perilaku sebenarnya kemungkinan besar bergantung pada banyak faktor (a.m
mungkin sama dengan 42, 0, atau sampah lainnya). Tempat-tempat~UB
yang disebutkan juga merupakan perilaku yang tidak ditentukan dalam teori, tetapi dalam praktiknya, karena penggunaan penempatan baru, sangat tidak mungkin daripadaa->m
akan sama dengan apa pun selain 42.sumber