อายุการใช้งานของตัวแปรแบบคงที่ในฟังก์ชัน C ++ คืออะไร


373

หากตัวแปรถูกประกาศstaticในขอบเขตของฟังก์ชั่นมันจะเริ่มต้นได้เพียงครั้งเดียวและเก็บค่าไว้ระหว่างการเรียกใช้ฟังก์ชัน อายุการใช้งานของมันคืออะไร? ตัวสร้างและ destructor ของมันถูกเรียกเมื่อใด

void foo() 
{ 
    static string plonk = "When will I die?";
}

คำตอบ:


257

อายุการใช้งานของstaticตัวแปรฟังก์ชั่นเริ่มต้นในครั้งแรก[0]การไหลของโปรแกรมพบการประกาศและมันจะจบลงที่การยกเลิกโปรแกรม ซึ่งหมายความว่าเวลาทำงานต้องดำเนินการเก็บรักษาหนังสือบางอย่างเพื่อทำลายมันเฉพาะเมื่อมันถูกสร้างขึ้นจริง

นอกจากนี้เนื่องจากมาตรฐานบอกว่า destructors ของวัตถุคงที่จะต้องทำงานในลำดับย้อนกลับของการก่อสร้างของพวกเขา[1]และลำดับของการก่อสร้างอาจขึ้นอยู่กับโปรแกรมเฉพาะที่เรียกใช้คำสั่งของการก่อสร้างจะต้องนำมาพิจารณา .

ตัวอย่าง

struct emitter {
    string str;
    emitter(const string& s) : str(s) { cout << "Created " << str << endl; }
    ~emitter() { cout << "Destroyed " << str << endl; }
};

void foo(bool skip_first) 
{
    if (!skip_first)
        static emitter a("in if");
    static emitter b("in foo");
}

int main(int argc, char*[])
{
    foo(argc != 2);
    if (argc == 3)
        foo(false);
}

เอาท์พุท:

C:> sample.exe
สร้างใน foo
ถูกทำลายใน foo

C:> sample.exe 1
สร้างในถ้า
สร้างใน foo
ถูกทำลายใน foo
ถูกทำลายในถ้า

C:> sample.exe 1 2
สร้างใน foo
สร้างในถ้า
ถูกทำลายในถ้า
ถูกทำลายใน foo

[0]เนื่องจากC ++ 98 [2]ไม่มีการอ้างอิงถึงหลายเธรดว่าสิ่งนี้จะทำงานอย่างไรในสภาวะแวดล้อมแบบมัลติเธรดที่ไม่ได้ระบุและอาจเป็นปัญหาได้เมื่อRoddyกล่าวถึง

[1] C ++ 98ส่วน3.6.3.1 [basic.start.term]

[2]ใน C ++ 11 สถิตจะเริ่มต้นในวิธีที่ปลอดภัยหัวข้อนี้ยังเป็นที่รู้จักกันเมจิกสถิตยศาสตร์


2
สำหรับประเภทที่เรียบง่ายที่ไม่มีผลข้างเคียง c'tor / d'tor มันเป็นการเพิ่มประสิทธิภาพที่ตรงไปตรงมาเพื่อเริ่มต้นพวกเขาในลักษณะเดียวกับประเภททั่วไปทั่วโลก วิธีนี้จะช่วยหลีกเลี่ยงการแตกกิ่งก้านธงและปัญหาการทำลายล้าง ไม่ได้หมายความว่าอายุการใช้งานของพวกเขาจะแตกต่างกัน
John McFarlane

1
หากฟังก์ชั่นสามารถเรียกใช้โดยหลายเธรดได้หมายความว่าคุณต้องตรวจสอบให้แน่ใจว่าการประกาศคงที่จะต้องได้รับการป้องกันโดย mutex ใน C ++ 98 ??
allyourcode

1
"destructors" ของวัตถุทั่วโลกจะต้องทำงานในลำดับย้อนหลังของการก่อสร้างเสร็จสมบูรณ์ "ไม่ได้ใช้ที่นี่เพราะวัตถุเหล่านี้ไม่ได้ทั่วโลก ลำดับการทำลายของชาวเมืองที่มีระยะเวลาการจัดเก็บแบบคงที่หรือแบบเธรดมีความซับซ้อนมากกว่า LIFO ดูหัวข้อ 3.6.3[basic.start.term]
Ben Voigt

2
วลี "เมื่อสิ้นสุดโปรแกรม" ไม่ถูกต้องอย่างเคร่งครัด สิ่งที่เกี่ยวกับสถิตใน Windows ที่กำลังโหลดและไม่โหลดแบบไดนามิก? เห็นได้ชัดว่ามาตรฐาน C ++ ไม่ได้เกี่ยวข้องกับการประกอบเลย (มันจะดีถ้ามันทำ) แต่การชี้แจงว่าสิ่งที่มาตรฐานพูดในที่นี้ดีหรือไม่ หากมีการรวมวลี "เมื่อสิ้นสุดโปรแกรม" มันจะทำให้การใช้งาน C ++ กับแอสเซมบลีที่ยกเลิกการโหลดแบบไดนามิกไม่สอดคล้องกัน
Roger Sanders

2
@Motti ฉันไม่เชื่อว่ามาตรฐานอนุญาตไลบรารีแบบไดนามิก แต่จนถึงขณะนี้ฉันยังไม่เชื่อว่ามีสิ่งใดเป็นพิเศษในมาตรฐานที่ขัดแย้งกับการใช้งาน แน่นอนว่าการพูดภาษาที่นี่อย่างเคร่งครัดไม่ได้ระบุว่าวัตถุคงไม่สามารถถูกทำลายได้ก่อนหน้านี้ด้วยวิธีการอื่นเพียงว่าพวกเขาจะต้องถูกทำลายเมื่อกลับจากหลักหรือเรียก std :: exit เป็นเส้นแบ่งที่สวย แต่ฉันคิดว่า
Roger Sanders

125

Motti นั้นถูกต้องเกี่ยวกับคำสั่งซื้อ แต่มีสิ่งอื่นที่ควรพิจารณา:

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

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

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


68
C ++ 0x ต้องการให้การเริ่มต้นคงที่นั้นปลอดภัยสำหรับเธรด ดังนั้นจงระแวดระวัง แต่สิ่งต่าง ๆ จะดีขึ้นเท่านั้น
deft_code

ปัญหาการสั่งทำลายสินค้าสามารถหลีกเลี่ยงได้ด้วยนโยบายเล็กน้อย วัตถุคงที่ / ทั่วโลก (ซิงเกิล ฯลฯ ) จะต้องไม่เข้าถึงวัตถุคงที่อื่น ๆ ในส่วนของวิธีการ พวกเขาจะต้องเข้าถึงได้เฉพาะในตัวสร้างซึ่งสามารถเก็บการอ้างอิง / ตัวชี้สำหรับการเข้าถึงในภายหลังในวิธี นี้ไม่สมบูรณ์ แต่ควรแก้ไข 99 กรณีและกรณีที่ไม่ได้จับชัดและน่าจะติดอยู่ในการตรวจสอบรหัส นี่ยังไม่เป็นการแก้ไขที่สมบูรณ์แบบเนื่องจากไม่สามารถบังคับใช้นโยบายในภาษาได้
deft_code

ฉันเป็นคนหูหนวก แต่ทำไมนโยบายนี้จึงบังคับใช้ในภาษาไม่ได้
cjcurrie

9
ตั้งแต่ C ++ 11 นี่ไม่ใช่ปัญหาอีกต่อไป คำตอบของ Motti ได้รับการปรับปรุงตามนั้น
Nilanjan Basu

10

คำอธิบายที่มีอยู่ยังไม่สมบูรณ์โดยไม่ต้องมีกฎจริงจากมาตรฐานพบใน 6.7:

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


8

FWIW, Codegear C ++ Builder ไม่ทำลายในลำดับที่คาดหวังตามมาตรฐาน

C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if

... ซึ่งเป็นอีกเหตุผลหนึ่งที่ไม่ต้องพึ่งพาคำสั่งทำลาย!


57
ไม่ใช่ข้อโต้แย้งที่ดี ฉันจะบอกว่านี่เป็นข้อโต้แย้งมากกว่าที่จะไม่ใช้คอมไพเลอร์นี้
Martin York

26
อืมมม หากคุณสนใจที่จะผลิตรหัสพกพาในโลกแห่งความเป็นจริงแทนที่จะเป็นเพียงรหัสพกพาตามหลักวิชาฉันคิดว่ามันมีประโยชน์ที่จะทราบว่าส่วนใดของภาษาที่สามารถทำให้เกิดปัญหาได้ ฉันจะแปลกใจถ้า C ++ Builder นั้นไม่เหมือนใครในการไม่จัดการสิ่งนี้
Roddy

17
ฉันเห็นด้วยยกเว้นว่าฉันใช้วลีว่า "ผู้รวบรวมสิ่งใดก่อให้เกิดปัญหาและภาษาที่พวกเขาทำในพื้นที่" ;-P
Steve Jessop

0

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

ตัวแปรคงที่จะถูกสร้างขึ้นในส่วนข้อมูลของหน่วยความจำ


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