วลีต่อไปนี้หมายความว่าอย่างไรใน C ++: zero-, default- และ value-initialization


190

วลีต่อไปนี้หมายความว่าอย่างไรใน C ++:

  • ศูนย์การเริ่มต้น

  • การกำหนดค่าเริ่มต้นและ

  • มูลค่าเริ่มต้น

นักพัฒนา C ++ ควรรู้อะไรเกี่ยวกับพวกเขาบ้าง


1
สิ่งนี้เกี่ยวข้องกับ (แต่ไม่เหมือนกัน) stackoverflow.com/questions/620137/ …
Steve Jessop

20
ยังมีอีก! รายการที่สมบูรณ์ของการเริ่มต้น: ค่า, ตรง, คัดลอก, รายการ (C ++ 11 อินโทรใหม่), รวม, การอ้างอิง, ศูนย์, ค่าคงที่และค่าเริ่มต้น; en.cppreference.com/w/cpp/language/initializationแสดงรายการทั้งหมดพร้อมตัวอย่าง :)
legends2k

คำตอบ:


65

สิ่งหนึ่งที่ต้องตระหนักคือ 'การกำหนดค่าเริ่มต้น' นั้นใหม่กับมาตรฐาน C ++ 2003 - มันไม่มีอยู่ในมาตรฐาน 1998 ดั้งเดิม (ฉันคิดว่ามันอาจเป็นความแตกต่างเพียงอย่างเดียวที่มากกว่าการชี้แจง) ดูคำตอบของ Kirill V. Lyadvinskyสำหรับคำจำกัดความที่ตรงจากมาตรฐาน

ดูคำตอบก่อนหน้านี้เกี่ยวกับพฤติกรรมของoperator newเพื่อดูรายละเอียดเกี่ยวกับพฤติกรรมที่แตกต่างกันของการเริ่มต้นประเภทนี้และเมื่อพวกเขาเตะเข้า (และเมื่อพวกเขาแตกต่างจาก c ++ 98 ถึง C ++ 03):

ประเด็นหลักของคำตอบคือ:

บางครั้งหน่วยความจำที่ส่งคืนโดยผู้ดำเนินการใหม่จะเริ่มต้นได้และบางครั้งมันจะไม่ขึ้นอยู่กับว่าประเภทที่คุณเพิ่งจะเริ่มเป็น POD หรือถ้าเป็นคลาสที่มีสมาชิก POD และกำลังใช้ตัวสร้างเริ่มต้นที่คอมไพเลอร์ .

  • ใน C ++ 1998 มีการกำหนดค่าเริ่มต้น 2 ประเภท: ศูนย์และค่าเริ่มต้น
  • ใน C ++ 2003 การเริ่มต้นประเภทที่ 3 จะมีการเพิ่มการกำหนดค่าเริ่มต้น

จะพูดอย่างน้อยก็ค่อนข้างซับซ้อนและเมื่อวิธีการต่าง ๆ เตะเข้าเฝ้า

สิ่งหนึ่งที่ต้องระวังอย่างแน่นอนคือ MSVC ปฏิบัติตามกฎ C ++ 98 แม้ใน VS 2008 (VC 9 หรือ cl.exe เวอร์ชัน 15.x)

ตัวอย่างต่อไปนี้แสดงให้เห็นว่า MSVC และ Digital Mars ปฏิบัติตามกฎ C ++ 98 ในขณะที่ GCC 3.4.5 และ Comeau ปฏิบัติตามกฎ C ++ 03:

#include <cstdio>
#include <cstring>
#include <new>

struct A { int m; }; // POD
struct B { ~B(); int m; }; // non-POD, compiler generated default ctor
struct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m

int main()
{
    char buf[sizeof(B)];
    std::memset( buf, 0x5a, sizeof( buf));

    // use placement new on the memset'ed buffer to make sure 
    //  if we see a zero result it's due to an explicit 
    //  value initialization
    B* pB = new(buf) B();   //C++98 rules - pB->m is uninitialized
                            //C++03 rules - pB->m is set to 0
    std::printf( "m  is %d\n", pB->m);
    return 0;
}

1
ไม่ใช่ว่ามันจะสำคัญสำหรับintแต่m()ในบรรทัดที่สามค่าเริ่มต้น m ที่สำคัญถ้าคุณเปลี่ยนไปint m; B m;:)
Johannes Schaub - litb

ใช่ - AและCไม่ได้ใช้ในตัวอย่างนี้ (เป็นคำตอบที่เชื่อมโยงกัน) แม้ว่า C ++ 98 และ C ++ 03 จะใช้คำศัพท์ต่างกันเมื่ออธิบายถึงวิธีการAและการCสร้างผลลัพธ์ที่ได้จะเหมือนกันในทั้งสองมาตรฐาน struct Bผลลัพธ์เฉพาะในลักษณะการทำงานที่แตกต่างกัน
Michael Burr

1
สิ่งที่ฉันหมายถึงคือถ้าคุณเปลี่ยน C เป็นstruct C { C() : m() {}; ~C(); B m; };แล้วคุณจะได้m.mเป็น 0 แต่ถ้ามันจะเป็นค่าเริ่มต้นmเหมือนที่คุณบอกว่า C ++ 03 m.mจะไม่เริ่มต้นเหมือนใน C ++ 98
Johannes Schaub - litb

1
ความคิดเห็นที่น่าสนใจเพิ่มเติมเกี่ยวกับการจัดการ MSVC ของคุณลักษณะนี้: stackoverflow.com/questions/3931312/…
Brent Bradburn

การเริ่มต้นเกิดขึ้นเมื่อคุณประกาศประเภทของคุณเป็นตัวแปรท้องถิ่นเช่นที่สแต็ก?
André Puel

89

C ++ 03 มาตรฐาน 8.5 / 5:

หากต้องการกำหนดค่าเริ่มต้นเป็นวัตถุประเภท T หมายถึง:
- ถ้า T เป็นประเภทสเกลาร์ (3.9) วัตถุจะถูกตั้งค่าเป็น 0 (ศูนย์) แปลงเป็น T;
- ถ้า T เป็นชนิดคลาสที่ไม่รวมสมาชิกแต่ละข้อมูลที่ไม่คงที่และแต่ละ subobject ระดับฐานเป็นศูนย์เริ่มต้นได้;
- ถ้า T เป็นชนิดยูเนี่ยนสมาชิกข้อมูลที่มีชื่อแรกของวัตถุนั้นจะถูกกำหนดค่าเริ่มต้นเป็นศูนย์
- ถ้า T เป็นชนิดอาร์เรย์แต่ละองค์ประกอบจะไม่มีค่าเริ่มต้น
- ถ้า T เป็นประเภทอ้างอิงจะไม่มีการเริ่มต้น

ในการเริ่มต้นเริ่มต้นวัตถุประเภท T หมายถึง:
- ถ้า T เป็นประเภทคลาสที่ไม่ใช่ POD (ข้อ 9) คอนสตรัคเตอร์เริ่มต้นสำหรับ T ถูกเรียก (และการเริ่มต้นนั้นเป็นรูปแบบที่ไม่ดีถ้า T
- ถ้า T เป็นชนิดอาร์เรย์แต่ละองค์ประกอบจะถูกกำหนดค่าเริ่มต้น
- มิฉะนั้นวัตถุจะไม่มีค่าเริ่มต้น

ในการกำหนดค่าเริ่มต้นวัตถุประเภท T หมายถึง:
- ถ้า T เป็นประเภทคลาส (ข้อ 9) ที่มีคอนสตรัคเตอร์ที่ผู้ใช้ประกาศ (12.1) ดังนั้นคอนสตรัคค่าเริ่มต้นสำหรับ T จะถูกเรียก (และการเตรียมใช้งานนั้น ไม่มีตัวสร้างเริ่มต้นที่สามารถเข้าถึงได้);
- ถ้า T เป็นชนิดคลาสที่ไม่รวมกันโดยไม่มีคอนสตรัคเตอร์ที่ผู้ใช้ประกาศดังนั้นสมาชิกข้อมูลที่ไม่คงที่และส่วนประกอบระดับฐานของ T จะถูกกำหนดค่าเริ่มต้น
- ถ้า T เป็นประเภทอาร์เรย์ดังนั้นแต่ละองค์ประกอบจะถูกกำหนดค่าเริ่มต้น
- มิฉะนั้นวัตถุจะไม่มีค่าเริ่มต้น

โปรแกรมที่เรียกใช้การเริ่มต้นเริ่มต้นหรือการกำหนดค่าเริ่มต้นของเอนทิตีของประเภทการอ้างอิงนั้นไม่ถูกต้อง หาก T เป็นชนิดที่ผ่านการรับรอง cv เวอร์ชัน T ที่ไม่ผ่านการรับรองจะถูกใช้สำหรับนิยามเหล่านี้ของการกำหนดค่าเริ่มต้นเป็นศูนย์การกำหนดค่าเริ่มต้นเริ่มต้นและการกำหนดค่าเริ่มต้น


18
สิ่งนี้อาจล้าสมัยสำหรับ C ++ 11 cppreference.comระบุว่าการเริ่มต้นเริ่มต้นไม่ได้เป็นสมาชิกเริ่มต้นเป็นศูนย์ (เฉพาะการเริ่มต้นค่าเท่านั้น)
Alexei Sholik

3
@ android เพิ่มจุดสำคัญซึ่งฉันไม่เห็นคำตอบที่อื่นดังนั้นฉันได้ทำคำถามใหม่ stackoverflow.com/questions/22233148/…
Adrian McCarthy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.