รายการ Initializer สำหรับวัตถุที่มีตัวสร้างเริ่มต้น

มีประโยชน์ใด ๆ ในการวางตัวแปรสมาชิกคลาสในรายการตัวเริ่มต้นที่ไม่จำเป็นต้องอยู่ในรายการตัวเริ่มต้นหรือไม่? ตัวอย่าง:

class Foo
{
public:
  Foo() {}
};

class Bar
{
public:
  Bar() : _foo() {}

private:
  Foo _foo;
};

คอมไพเลอร์ทำอะไรเป็นพิเศษในกรณีนี้หรือไม่?


comment
_foo ได้รับการเริ่มต้นแล้ว (โดยใช้ตัวสร้างเริ่มต้นของ foo) จากการประกาศ Foo _foo; หากคุณต้องการกำหนดค่าเริ่มต้นใหม่ _foo ให้ใช้ Bar() : _foo(Foo()) {} อย่างไรก็ตาม นี่คงจะไร้จุดหมาย   -  person andre    schedule 05.11.2012
comment
@ahenderson: นั่นไม่ถูกต้อง _foo ได้รับการเตรียมใช้งานในรายการเครื่องมือเริ่มต้น นั่นคือที่เดียวเท่านั้นที่สมาชิกจะเริ่มต้นได้ในชั้นเรียน Bar() : _foo(Foo()) {} ไม่ เริ่มต้นใหม่ _foo -- เริ่มต้น   -  person John Dibling    schedule 05.11.2012
comment
@JohnDibling โอ้ ตอนนั้นฉันคิดผิด ตอนนี้ฉันเข้าใจแล้วจากการอ่านคำตอบ Kerrek SB   -  person andre    schedule 05.11.2012


คำตอบ (4)


รายการตัวเริ่มต้นเป็นวิธีที่ต้องการในการกำหนดค่าเริ่มต้นให้กับสมาชิกในตัวสร้าง มันเป็นวิธีเดียวที่เป็นไปได้ในการเริ่มต้นสมาชิกอ้างอิงและสมาชิกคงที่

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

person Syntactic Fructose    schedule 05.11.2012
comment
ควรสังเกตว่ารายการควรอยู่ในลำดับเดียวกับการประกาศ ถือเป็นแนวปฏิบัติที่ดี - person Ed Heal; 05.11.2012
comment
สมาชิกอ้างอิงและค่าคงที่สามารถเริ่มต้นได้โดยใช้การเริ่มต้นในคลาส (C ++ 11) และสำหรับประเภทที่เป็นปัญหา รายการตัวเริ่มต้นจะไม่ส่งผลต่อว่าสมาชิกจะสามารถเข้าถึงได้ก่อนที่จะเริ่มต้นหรือไม่ สมาชิกจะถูกเตรียมใช้งานเริ่มต้นในเวลาเดียวกันในทั้งสองกรณี - person bames53; 05.11.2012
comment
@EdHeal มันเป็นมากกว่าการฝึกฝนที่ดีเล็กน้อย พวกมันถูกเตรียมใช้งานตามลำดับนั้น ดังนั้นหากคุณเตรียมใช้งานอันหนึ่งโดยอิงจากอันอื่น คุณควรใส่อันที่ขึ้นต่อกันตามอันอิสระในคลาส - person chris; 05.11.2012
comment
@chris - ใช่ฉันรู้ - แค่อยากทำยาให้หวาน สกอตต์ ไมเยอร์ส. จำเลขในเล่มแรกในหัวไม่ได้เลย - person Ed Heal; 05.11.2012

ในกรณีนี้มันไม่สร้างความแตกต่าง

แต่การดำเนินการดังกล่าวอาจเป็นประโยชน์
หากคุณมีสมาชิกจำนวนมาก การมีสมาชิกเพียงบางส่วนแต่ไม่ใช่ทั้งหมดในรายการอาจทำให้เกิดความสับสนได้ นอกจากนี้ยังตอกย้ำลำดับการเริ่มต้นในใจของคุณ (ลำดับถูกกำหนดโดยลำดับการประกาศในคลาส แต่จะมีประโยชน์ในการมองเห็นลำดับนี้ในคลาสที่ใหญ่กว่าโดยที่ตัวแปรสมาชิกบางตัวไม่ได้ประกาศอยู่ข้างๆ กัน)

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

อันตรายที่แท้จริงอยู่ที่คลาสที่มีสมาชิก POD และคอนสตรัคเตอร์ที่สร้างคอมไพเลอร์

class NewFoo
{
      int x;
      int y;
};
// Version 1:
class Bar1
{
     NewFoo  f;
};
// Version 2:
class Bar2
{
     NewFoo  f;
     public:
         Bar2()  // f not in list.
         {}
};
// Version 3:
class Bar3
{
     NewFoo  f;
     public:
         Bar3()
            : f()
         {}
};

int main()
{
     Bar1     b1a;           // x and y not  initialized.
     Bar1     b1b = Bar1();  // x and y zero initialized.
     Bar2     b2a;           // x and y not  initialized.
     Bar2     b2b = Bar2();  // x and y not  initialized.
     Bar3     b3a;           // x and y zero initialized.
     Bar3     b3b = Bar3();  // x and y zero initialized.
}
person Martin York    schedule 05.11.2012
comment
+1 สำหรับคำตอบที่แท้จริงนี้ ฉันทำตามสไตล์ที่คุณแนะนำมาหลายปีแล้ว แต่ตอนนี้ฉันสงสัยว่าการที่สมาชิกพูดซ้ำๆ ซากๆ อาจเป็นแค่เสียงรบกวนหรือเปล่า สำหรับคลาสที่มีลักษณะคล้ายโครงสร้างที่มีเพียงสมาชิกที่มีคอนสตรัคเตอร์เริ่มต้น จะดีกว่าไหมถ้าใช้รายการ Initializer ว่างๆ - person Wolf; 04.04.2014
comment
@หมาป่า: ใช่ นี่คือบริบททั้งหมด คุณควรใช้เทคนิคที่ทำให้โค้ดอ่านง่ายขึ้น หากเห็นได้ชัดว่าคลาสของคุณมีสมาชิกที่สร้างขึ้นโดยอัตโนมัติ แค่มีรายการว่างก็ไม่เป็นไร - person Martin York; 04.04.2014
comment
@Wolf: อัปเดตคำตอบพร้อมข้อมูล - person Martin York; 04.04.2014
comment
Bar3(): f() {} - สิ่งนี้ไม่ได้ทำอะไรกับคอมไพเลอร์ของฉัน (x, y ไม่ได้กำหนดไว้) - นี่เป็นคุณสมบัติ C ++ 11 หรือไม่ - person Wolf; 04.04.2014
comment
@Wolf: ไม่นี่คือคุณสมบัติ C ++ 03 เพราะคุณเรียกตัวเริ่มต้นของ f อย่างชัดเจน สิ่งนี้จะเรียกเวอร์ชันเริ่มต้นเป็นศูนย์ของคอนสตรัคเตอร์ที่สร้างคอมไพเลอร์ ทำไมคุณถึงคิดว่าพวกเขาไม่ได้ทำอะไรเลย? คุณใช้คอมไพเลอร์ตัวไหน? - person Martin York; 04.04.2014
comment
พฤติกรรม C ++ 03 สมเหตุสมผลอย่างยิ่ง ฉันส่วนใหญ่ (และเกลียด) ใช้คอมไพเลอร์ BCB6 (Borland C++ 5.6.4) และมี เห็นได้ชัดว่ามีข้อบกพร่องบางอย่างในรายการตัวเริ่มต้น ;) - person Wolf; 04.04.2014
comment
ฉันเรียกใช้ตัวอย่างนี้ใน Visual C++ 2013 และไม่มีกรณีใดที่ x หรือ y เป็นศูนย์ นี่เป็นความไม่สอดคล้องของ VC++ หรือไม่ - person Peter B; 13.08.2015
comment
ใน Visual Studio 2017 ทำงานได้ตามที่ระบุไว้ในความคิดเห็น ขอบคุณสำหรับตัวอย่าง - person Jarek C; 20.02.2019

สมาชิกที่ไม่ได้กล่าวถึงในรายการตัวสร้างเริ่มต้นของตัวสร้างที่กำลังใช้งานอยู่จะถูกกำหนดค่าเริ่มต้นเป็นค่าเริ่มต้น สำหรับ Foo นี่หมายความว่าตัวสร้างเริ่มต้นจะถูกเรียก

ความแตกต่างระหว่าง Bar() : _foo() { } และการไม่ให้ Bar เป็นคอนสตรัคเตอร์เริ่มต้นที่ชัดเจนเลย (หรือบอกว่า Bar() = default ก็คือเวอร์ชันของคุณไม่มีคอนสตรัคเตอร์เริ่มต้นเล็กน้อย ดังนั้นค่าของ std::is_trivially_constructible<Bar>::value จะแตกต่างจากถ้าคุณปล่อยคอนสตรัคเตอร์ออกไปโดยสิ้นเชิง แม้ว่าพฤติกรรมจะเปลี่ยนไปก็ตาม เป็นอย่างอื่นเหมือนกัน

person Kerrek SB    schedule 05.11.2012
comment
ฉันเดาว่าคำถามอยู่ที่ความแตกต่างระหว่างคอนสตรัคเตอร์ที่ผู้ใช้ระบุโดยมีและไม่มีตัวเริ่มต้นในรายการตัวเริ่มต้นสำหรับสมาชิกที่เป็นปัญหา ไม่ใช่ความแตกต่างระหว่างคอนสตรัคเตอร์ที่ผู้ใช้ระบุและคอนสตรัคเตอร์ที่ผู้ใช้ไม่ได้ระบุ - person bames53; 05.11.2012

ไม่ ในกรณีนี้ไม่มีอะไรแตกต่างออกไป ฉันไม่เห็นประโยชน์ใด ๆ ในการทำเช่นนี้

person bames53    schedule 05.11.2012
comment
ข้อดีก็คือ คุณจะไม่สับสนกับคนที่ได้รับการฝึกอบรมให้มองหารายการเครื่องมือเริ่มต้น - person Cubic; 05.11.2012