ลำดับของตัวสร้างสมาชิกและการเรียกตัวทำลาย


121

โอ้ผู้เชี่ยวชาญด้าน C ++ ฉันแสวงหาภูมิปัญญาของคุณ พูดมาตรฐานกับฉันและบอกว่า C ++ รับประกันว่าโปรแกรมต่อไปนี้:

#include <iostream>
using namespace std;

struct A
{
    A() { cout << "A::A" << endl; }
    ~A() { cout << "A::~" << endl; }
};

struct B
{
    B() { cout << "B::B" << endl; }
    ~B() { cout << "B::~" << endl; }
};

struct C
{
    C() { cout << "C::C" << endl; }
    ~C() { cout << "C::~" << endl; }
};

struct Aggregate
{
    A a;
    B b;
    C c;
};

int main()
{
    Aggregate a;
    return 0;
}

จะผลิตเสมอ

A::A
B::B
C::C
C::~
B::~
A::~

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


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

คำตอบ:


140

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

ใช่ทั้งสองอย่าง ดู 12.6.2

6 การเริ่มต้นจะดำเนินการตามลำดับต่อไปนี้:

  • อันดับแรกและสำหรับคอนสตรัคเตอร์ของคลาสที่ได้รับมากที่สุดตามที่อธิบายไว้ด้านล่างคลาสพื้นฐานเสมือนจะได้รับการเริ่มต้นตามลำดับที่ปรากฏบนการข้ามจากซ้ายไปขวาที่มีความลึกเป็นอันดับแรกของกราฟอะไซคลิกที่กำหนดทิศทางของคลาสพื้นฐานโดยที่ "ซ้าย -to-right” คือลำดับการปรากฏของชื่อคลาสฐานในคลาส base-specifier-list ที่ได้รับมา

  • จากนั้นคลาสพื้นฐานโดยตรงจะถูกเริ่มต้นตามลำดับการประกาศตามที่ปรากฏในรายการตัวระบุฐาน (โดยไม่คำนึงถึงลำดับของตัวเริ่มต้นของ mem)

  • จากนั้นสมาชิกข้อมูลที่ไม่คงที่จะถูกเตรียมใช้งานตามลำดับที่ประกาศไว้ในนิยามคลาส (อีกครั้งโดยไม่คำนึงถึงลำดับของตัวเริ่มต้น mem)

  • ในที่สุดสารประกอบ - คำสั่งของตัวสร้างจะถูกดำเนินการ [หมายเหตุ: คำสั่งการประกาศได้รับคำสั่งเพื่อให้แน่ใจว่าวัตถุฐานและวัตถุย่อยของสมาชิกถูกทำลายตามลำดับการเริ่มต้น - ส่งหมายเหตุ]


2
ถ้าจำไม่ผิดใช่ทั้งคู่ ... คิดว่าเป็นปึก ดันแรกโผล่ล่าสุด ดังนั้นเมื่ออินสแตนซ์อินสแตนซ์แรกของคุณอินสแตนซ์จะถูกผลักเข้าไปในหน่วยความจำตามลำดับสแต็ก จากนั้นครั้งที่สองจะถูกผลักไปที่สามในครั้งที่สองและอื่น ๆ จากนั้นเมื่อทำลายอินสแตนซ์ของคุณโปรแกรมจะมองหาสิ่งแรกที่จะทำลายครั้งสุดท้ายที่ถูกผลัก แต่ฉันอาจจะอธิบายผิดด้วยวิธีนี้ แต่เป็นวิธีที่ฉันเรียนรู้เมื่อทำ C / C ++ และ ASM
Will Marcouiller

29

ใช่พวกเขาเป็น (สมาชิกที่ไม่คงที่นั่นคือ) ดู 12.6.2 / 5 สำหรับการเริ่มต้น (การก่อสร้าง) และ 12.4 / 6 สำหรับการทำลายล้าง


10

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

struct A { };

struct B {
 A &a;
 B(A& a) : a(a) { }
};

int main() {
    A a;
    B b(a);
}

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


ฉันไม่เคยคิดว่ากฎนี้ใช้กับคำสั่งทำลายของสมาชิกขอบเขตด้วย!
yano

6

ใช่และใช่ ลำดับการทำลายล้างจะตรงข้ามกับลำดับการสร้างเสมอสำหรับตัวแปรสมาชิก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.