ตัวแปรสแตติกในฟังก์ชันสมาชิก


158

ใครช่วยกรุณาอธิบายว่าตัวแปรสแตติกในฟังก์ชันสมาชิกทำงานใน C ++ ได้อย่างไร

รับคลาสต่อไปนี้:

class A {
   void foo() {
      static int i;
      i++;
   }
}

หากฉันประกาศหลายอินสแตนซ์การAเรียกfoo()ใช้อินสแตนซ์หนึ่งเพิ่มตัวแปรสแตติกiในทุกอินสแตนซ์หรือไม่ หรือเพียงอันเดียวที่ถูกเรียกใช้?

ฉันสันนิษฐานว่าแต่ละอินสแตนซ์จะมีสำเนาของตัวเองiแต่ก้าวผ่านโค้ดบางอย่างฉันดูเหมือนจะบ่งบอกเป็นอย่างอื่น

คำตอบ:


169

เนื่องจากclass Aเป็นคลาสที่ไม่ใช่แม่แบบและA::foo()เป็นฟังก์ชันที่ไม่ใช่แม่แบบ จะมีเพียงหนึ่งสำเนาstatic int iภายในโปรแกรม

อินสแตนซ์ของAวัตถุใด ๆ ที่จะส่งผลกระทบต่ออายุการiใช้งานของโปรแกรมiจะยังคงเหมือนเดิม ในการเพิ่มตัวอย่าง:

A o1, o2, o3;
o1.foo(); // i = 1
o2.foo(); // i = 2
o3.foo(); // i = 3
o1.foo(); // i = 4

3
ขอบคุณสำหรับตัวอย่างที่ดี! จะมีวิธีที่จะบรรลุสิ่งที่ทำให้ขอบเขตของตัวอย่างที่static int iเฉพาะเจาะจงเช่นนั้นo1.foo(); // i = 1และ$o2.foo(); // i = 1... ?
Stingery

14
แม้ว่านี่อาจไม่ใช่สไตล์ที่คุณกำลังมองหาการทำให้ ia ข้อมูลส่วนตัวของสมาชิกคลาส A น่าจะมีเอฟเฟกต์ที่คุณกำลังอธิบาย หากคุณกังวลเกี่ยวกับความขัดแย้งของชื่อคุณสามารถเพิ่มคำนำหน้าเช่นm_เพื่อระบุสถานะของ i
Carl Morris

137

คำหลักstaticน่าเสียดายที่มีความหมายที่ไม่เกี่ยวข้องแตกต่างกันเล็กน้อยใน C ++

  1. เมื่อใช้สำหรับสมาชิกข้อมูลหมายความว่ามีการจัดสรรข้อมูลในคลาสและไม่ได้อยู่ในอินสแตนซ์

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

  3. เมื่อนำมาใช้ในระดับที่รวบรวมหน่วย (โมดูล) ก็หมายความว่าตัวแปรเป็นเหมือนทั่วโลก (จัดสรร ie และเริ่มต้นก่อนที่จะmainมีการเรียกใช้และทำลายหลังจากmainออก) แต่ที่ตัวแปรจะไม่สามารถเข้าถึงหรือมองเห็นได้ในหน่วยสะสมอื่น

ฉันได้เพิ่มการเน้นในส่วนที่สำคัญที่สุดสำหรับการใช้งานแต่ละครั้ง ใช้ (3) ค่อนข้างท้อใจในความโปรดปรานของ namespaces ที่ไม่มีชื่อที่ยังช่วยให้การประกาศคลาสยกเลิกการส่งออก

ในรหัสของคุณstaticคำหลักจะใช้กับความหมายหมายเลข 2 และไม่เกี่ยวข้องกับคลาสหรืออินสแตนซ์ ... มันเป็นตัวแปรของฟังก์ชันและจะมีเพียงหนึ่งสำเนาเท่านั้น

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

#include <stdio.h>

template<int num>
void bar()
{
    static int baz;
    printf("bar<%i>::baz = %i\n", num, baz++);
}

int main()
{
    bar<1>(); // Output will be 0
    bar<2>(); // Output will be 0
    bar<3>(); // Output will be 0
    bar<1>(); // Output will be 1
    bar<2>(); // Output will be 1
    bar<3>(); // Output will be 1
    bar<1>(); // Output will be 2
    bar<2>(); // Output will be 2
    bar<3>(); // Output will be 2
    return 0;
}

41
+1 สำหรับkeyword static unfortunately has a few different unrelated meanings in C++:)
iammilind

โลกมีความหมายมากขึ้นหลังจากอ่านสิ่งนี้ขอบคุณ
Erin

ฉันชอบเคล็ดลับกับแม่แบบ ฉันรอไม่ไหวที่จะหาข้อแก้ตัวที่จะใช้มัน
Tomáš Zato - Reinstate Monica

ทุกคนมีการอ้างอิงสำหรับ "ค่อนข้างท้อใจแก่ namespaces ที่ไม่มีชื่อ"?
austinmarton

3
@austinmarton: วลี "การใช้สแตติกเพื่อระบุ 'local to translation unit' ถูกคัดค้านใน C ++ ใช้ namespaces ที่ไม่มีชื่อแทน (8.2.5.1)" มีอยู่ในภาษาโปรแกรม C ++ ในรุ่นของฉัน (ฉบับที่ 10, กันยายน 1999) ที่หน้า 819
6502

2

ตัวแปรแบบคงที่ภายในฟังก์ชั่น

  • ตัวแปรแบบคงที่ถูกสร้างขึ้นภายในฟังก์ชั่นถูกเก็บไว้ในหน่วยความจำแบบคงที่ของโปรแกรมไม่ได้อยู่ในสแต็ก

  • การกำหนดค่าเริ่มต้นตัวแปรแบบสแตติกจะดำเนินการในการเรียกใช้ครั้งแรกของฟังก์ชัน

  • ตัวแปรแบบคงที่จะเก็บค่าไว้ในการเรียกใช้ฟังก์ชันหลายครั้ง

  • อายุการใช้งานของตัวแปรคงที่คือโปรแกรม

ป้อนคำอธิบายรูปภาพที่นี่

ตัวอย่าง

#include <iostream>

using namespace std;

class CVariableTesting 
{
    public:
    
    void FuncWithStaticVariable();
    void FuncWithAutoVariable();

};

void CVariableTesting::FuncWithStaticVariable()
{
    static int staticVar = 0; //staticVar is initialised by 0 the first time
    cout<<"Variable Value : "<<staticVar<<endl;
    staticVar++;
}
void CVariableTesting::FuncWithAutoVariable()
{
    int autoVar = 0;
    cout<<"Variable Value : "<<autoVar<<endl;
    autoVar++;
}
    

int main()
{
    CVariableTesting objCVariableTesting;
    cout<<"Static Variable";
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    objCVariableTesting.FuncWithStaticVariable();
    
    cout<<endl;
    cout<<"Auto Variable";
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    objCVariableTesting.FuncWithAutoVariable();
    
    return 0;
}

ผลผลิต:

ตัวแปรคงที่

ค่าตัวแปร: 0
ค่าตัวแปร: 1
ค่าตัวแปร: 2
ค่าตัวแปร: 3
ค่าตัวแปร: 4

ตัวแปรอัตโนมัติ

ค่าตัวแปร: 0
ค่าตัวแปร: 0
ค่าตัวแปร: 0
ค่าตัวแปร: 0
ค่าตัวแปร: 0


-2

คำตอบที่ง่าย:

ตัวแปรสแตติกไม่ว่าพวกเขาจะเป็นสมาชิกของclassฟังก์ชั่น (ที่ไม่ใช่เทมเพลต) หรือฟังก์ชั่นที่ไม่ใช่เทมเพลตพฤติกรรมเช่นเดียวกับฉลากระดับโลกที่ จำกัด ขอบเขตclassหรือฟังก์ชั่น


9
ไม่ Globals ถูกเตรียมใช้งานเมื่อเริ่มต้นโปรแกรมสถิติฟังก์ชันจะเริ่มต้นได้เมื่อใช้ครั้งแรก นี่คือความแตกต่างที่ยิ่งใหญ่
6502

ฉันไม่คิดว่านี่คือสิ่งที่เกิดขึ้น อย่างไรก็ตามนี่ควรเป็นคอมไพเลอร์เฉพาะ
0xbadf00d

2
ถ้าอย่างนั้นคุณคิดว่าผิด: 3.6.1 ในมาตรฐาน C ++ กำหนดว่าการสร้างวัตถุของขอบเขตเนมสเปซที่มีระยะเวลาการจัดเก็บแบบคงที่เกิดขึ้นเมื่อเริ่มต้น; 6.7 (4) กำหนดว่าโดยทั่วไป "... เช่นตัวแปรจะเริ่มต้นเมื่อการควบคุมครั้งแรกผ่านการประกาศของมันตัวแปรดังกล่าวจะถือว่าเริ่มต้นเมื่อเสร็จสิ้นการเริ่มต้น" โดยวิธีการเริ่มต้นในการใช้งานครั้งนี้มีประโยชน์มากในการดำเนินการก่อสร้างขี้เกียจเดี่ยว
6502

3.7.4: "การเริ่มต้นอย่างต่อเนื่อง (3.6.2) ของเอนทิตีขอบเขตบล็อกที่มีระยะเวลาการจัดเก็บแบบสแตติกถ้าทำได้จะดำเนินการก่อนที่บล็อกจะถูกป้อนครั้งแรกการใช้งานที่ได้รับอนุญาตให้ดำเนินการเริ่มต้นตัวแปรตัวแปรขอบเขตบล็อกอื่น ๆ ระยะเวลาการจัดเก็บแบบสแตติกหรือแบบเธรดภายใต้เงื่อนไขเดียวกันกับที่อนุญาตให้นำไปใช้งานเพื่อเริ่มต้นตัวแปรแบบคงที่ด้วยระยะเวลาการเก็บแบบคงที่หรือแบบเธรดในขอบเขตเนมสเปซ (3.6.2) มิเช่นนั้น
0xbadf00d

1
อย่างไรก็ตามอยากรู้อยากเห็นพอ: 1) สำหรับการเริ่มต้นอย่างต่อเนื่องมันไม่เกี่ยวข้องคุยกันว่าคงที่ในท้องถิ่นสามารถเริ่มต้นได้ก่อนที่จะเข้าบล็อกครั้งแรก (ตัวแปรจะมองเห็นได้เฉพาะในบล็อกและเริ่มต้นคงที่ผลิตไม่มีผลข้างเคียง); 2) ไม่มีการพูดถึงโพสต์ของคุณเกี่ยวกับการเริ่มต้นอย่างต่อเนื่อง; 3) สถิตศาสตร์ท้องถิ่นมีประโยชน์อย่างมากสำหรับการเริ่มต้นที่ไม่คงที่เช่นMyClass& instance(){ static MyClass x("config.ini"); return x; }- การใช้งานแบบพกพาที่ถูกต้องสำหรับการใช้เธรดเดียวอย่างแน่นอนเพราะสถิติท้องถิ่นไม่เหมือนทั่วโลกแม้จะเป็นสิ่งที่คุณพูด
6502
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.