คำหลักคงที่และการใช้งานที่หลากหลายใน C ++


196

คำหลักstaticคือคำที่มีความหมายหลายอย่างใน C ++ ที่ฉันพบว่าสับสนมากและฉันไม่สามารถงอใจได้ว่ามันควรจะทำงานอย่างไร

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

C ++ Standard กล่าวสิ่งนี้สำหรับสมาชิกคลาสข้อมูลที่มีคีย์เวิร์ดstatic:

3.7.1 ระยะเวลาการจัดเก็บแบบคงที่ [basic.stc.static]

3 คำสำคัญคงที่สามารถใช้ในการประกาศตัวแปรท้องถิ่นที่มีระยะเวลาการจัดเก็บแบบคงที่

4 สแตติกคำสำคัญที่ใช้กับสมาชิกคลาสข้อมูลในคำจำกัดความคลาสให้ระยะเวลาการเก็บข้อมูลสมาชิกแบบคงที่

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

นอกจากนี้ยังพูดถึงระยะเวลาในการจัดเก็บเฉพาะสมาชิกชั้นเรียนว่าเป็นเรื่องที่ไม่เฉพาะเจาะจงซึ่งเป็นคุณสมบัติที่staticไม่ หรือว่าเป็นระยะเวลาการเก็บรักษา?

ตอนนี้สิ่งที่เกี่ยวกับกรณีที่มีstaticและขอบเขตไฟล์? ตัวแปรส่วนกลางทั้งหมดถูกพิจารณาว่ามีระยะเวลาการเก็บแบบสแตติกตามค่าเริ่มต้นหรือไม่ ดูเหมือนว่าต่อไปนี้ (จากส่วน 3.7.1) ดังต่อไปนี้:

1 ตัวแปรทั้งหมดที่ไม่มีระยะเวลาการจัดเก็บแบบไดนามิกไม่มีระยะเวลาการจัดเก็บด้ายและไม่ท้องถิ่นมีระยะเวลาการจัดเก็บแบบคงที่ ที่เก็บข้อมูลสำหรับเอนทิตีเหล่านี้จะคงอยู่ตลอดระยะเวลาของโปรแกรม (3.6.2, 3.6.3)

วิธีการที่ไม่staticเกี่ยวข้องกับการเชื่อมโยงของตัวแปร?

staticคำหลักทั้งหมดนี้ทำให้เกิดความสับสนอย่างจริงจังใครบางคนสามารถอธิบายการใช้ภาษาอังกฤษที่แตกต่างกันและบอกฉันเมื่อใดที่จะเริ่มstaticสมาชิกคลาส?


คำตอบ:


147

ตัวแปร:

staticมีตัวแปรสำหรับ "อายุการใช้งาน" ของหน่วยการแปลที่กำหนดไว้และ:

  • หากอยู่ในขอบเขตเนมสเปซ (เช่นอยู่นอกฟังก์ชั่นและคลาส) จะไม่สามารถเข้าถึงได้จากหน่วยการแปลอื่น ๆ สิ่งนี้เรียกว่า "การเชื่อมโยงภายใน" หรือ "ระยะเวลาการจัดเก็บแบบคงที่" (อย่าทำสิ่งนี้ในส่วนหัวยกเว้นconstexprสิ่งอื่นและคุณท้ายด้วยตัวแปรแยกต่างหากในแต่ละหน่วยการแปลซึ่งสับสนอย่างมาก)
  • ถ้ามันเป็นตัวแปรในฟังก์ชั่นมันไม่สามารถเข้าถึงได้จากนอกฟังก์ชั่นเช่นเดียวกับตัวแปรท้องถิ่นอื่น ๆ (นี่คือท้องถิ่นที่พวกเขาพูดถึง)
  • สมาชิกคลาสไม่มีขอบเขตที่ จำกัด เนื่องจากstaticแต่สามารถแก้ไขได้จากคลาสรวมถึงอินสแตนซ์ (เช่นstd::string::npos) [หมายเหตุ: คุณสามารถประกาศสมาชิกแบบคงที่ในชั้นเรียน แต่โดยปกติแล้วพวกเขาควรจะยังคงถูกกำหนดในหน่วยการแปล (ไฟล์ cpp) และเช่นนี้มีเพียงหนึ่งต่อชั้น]

สถานที่เป็นรหัส:

static std::string namespaceScope = "Hello";
void foo() {
    static std::string functionScope= "World";
}
struct A {
   static std::string classScope = "!";
};

ก่อนที่ฟังก์ชันใด ๆ ในหน่วยการแปลจะถูกดำเนินการ (อาจเกิดmainขึ้นหลังจากเริ่มการประมวลผล) ตัวแปรที่มีระยะเวลาการจัดเก็บแบบคงที่ (ขอบเขตเนมสเปซ) ในหน่วยการแปลนั้นจะ "คงค่าเริ่มต้นคงที่" ( constexprเท่าที่เป็นไปได้ ชาวบ้านจะ "เริ่มต้นแบบไดนามิก" อย่างถูกต้องตามลำดับที่กำหนดไว้ในหน่วยการแปล (สำหรับสิ่งstd::string="HI";ที่ไม่เหมือนconstexpr) ในที่สุด statics ฟังก์ชั่นในท้องถิ่นจะเริ่มต้นในการดำเนินการครั้งแรก "ถึง" บรรทัดที่พวกเขาจะประกาศ ทั้งหมดstaticตัวแปรทำลายทั้งหมดในการสั่งซื้อย้อนกลับของการเริ่มต้น

วิธีที่ง่ายที่สุดที่จะได้รับสิทธิทั้งหมดนี้คือการทำให้ตัวแปรคงที่ทั้งหมดที่ไม่ได้constexprเริ่มต้นในการทำงานชาวบ้านคงที่ซึ่งจะทำให้แน่ใจว่าทั้งหมดของสถิตของคุณ / Globals จะเริ่มต้นอย่างถูกต้องเมื่อคุณพยายามที่จะใช้พวกเขาไม่ว่าสิ่งที่ทำให้การป้องกันการเริ่มต้นคงที่ ความล้มเหลวการสั่งซื้อ

T& get_global() {
    static T global = initial_value();
    return global;
}

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

ฟังก์ชั่น

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

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

struct A {
    A() {++A_count;}
    A(const A&) {++A_count;}
    A(A&&) {++A_count;}
    ~A() {--A_count;}

    static int get_count() {return A_count;}
private:
    static int A_count;
}

int main() {
    A var;

    int c0 = var.get_count(); //some compilers give a warning, but it's ok.
    int c1 = A::get_count(); //normal way
}

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

  • สามารถใช้ในไฟล์ cpp เพื่อรับประกันว่าจะไม่ใช้ฟังก์ชันจากไฟล์อื่น
  • สามารถใส่ในส่วนหัวและทุกไฟล์จะมีสำเนาของฟังก์ชั่นของตัวเอง ไม่มีประโยชน์เนื่องจาก inline ทำสิ่งเดียวกัน
  • เพิ่มความเร็วลิงค์เวลาโดยลดการทำงาน
  • สามารถใส่ฟังก์ชั่นที่มีชื่อเดียวกันในแต่ละหน่วยการแปลและพวกเขาสามารถทำสิ่งที่แตกต่าง ตัวอย่างเช่นคุณสามารถใส่static void log(const char*) {}ไฟล์ cpp แต่ละไฟล์และพวกเขาสามารถบันทึกทั้งหมดในวิธีที่แตกต่างกัน

1
สมาชิกชั้นเรียนล่ะ? ไม่ใช่กรณีที่สามแยกกันใช่ไหม
Étienne

4
@Etienne - สมาชิกข้อมูลระดับคงที่จะเหมือนกับตัวแปรโกลบอลแบบคงที่ยกเว้นว่าคุณสามารถเข้าถึงได้จากหน่วยการแปลอื่นและการเข้าถึงใด ๆ (ยกเว้นจากฟังก์ชั่นสมาชิก) จะต้องระบุclassname::ขอบเขต ฟังก์ชั่นสมาชิกระดับคงที่เป็นเหมือนฟังก์ชั่นทั่วโลก แต่ขอบเขตไปที่ชั้นเรียนหรือเหมือนสมาชิกปกติ แต่ไม่มีthis(นั่นไม่ใช่ทางเลือก - ทั้งสองควรจะเทียบเท่า)
Steve314

1
@ LuchianGrigore: ในขณะที่ฉันเห็นจุดของคุณฉันไม่แน่ใจว่าจะใช้ถ้อยคำอะไร
Mooing Duck

1
@ Steve314: ฉันเข้าใจสิ่งที่คุณหมายถึง แต่เมื่อจัดการกับคำที่มากเกินไปอย่างน่ากลัวเป็นแบบคงที่ฉันหวังว่าเราทุกคนระมัดระวังมากขึ้น โดยเฉพาะอย่างยิ่งทุกคนทั่วโลก (จริงๆระดับ namespace) ตัวแปรที่มีระยะเวลาคงที่เพื่อเพิ่มคงที่ในตัวแปรทั่วโลกคงอาจจะเข้าใจว่าเป็นnamespace A { static int x; }ซึ่งหมายถึงการเชื่อมโยงภายในและมีความแตกต่างจากลักษณะการทำงานของข้อมูลสมาชิกระดับคงที่
David Rodríguez - dribeas

1
"ถ้าอยู่ในขอบเขตเนมสเปซก็จะไม่สามารถเข้าถึงได้จากหน่วยการแปลอื่น ... " คุณหมายถึงอะไรถ้าอยู่ในขอบเขตเนมสเปซ ไม่ใช่ทุกกรณีใช่ไหมคุณสามารถยกตัวอย่างและตัวอย่างที่เคาน์เตอร์ได้หรือไม่?
AturSams

66

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

การเชื่อมโยงเป็นมุมฉากกับสิ่งนี้

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

คำสำคัญstaticสามารถใช้เพื่อแสดงถึงการเชื่อมโยงภายในและการจัดเก็บแบบคงที่ แต่ในสาระสำคัญเหล่านี้จะแตกต่างกัน

มันหมายความว่าอะไรกับตัวแปรท้องถิ่น? นั่นคือฟังก์ชั่นตัวแปรท้องถิ่น?

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

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

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

คงที่เกี่ยวข้องกับการเชื่อมโยงของตัวแปรอย่างไร

มันให้ตัวแปรเชื่อมโยงภายในเนมสเปซ มันให้สมาชิกและตัวแปรท้องถิ่นระยะเวลาการจัดเก็บแบบคงที่

ลองขยายทั้งหมด:

//

static int x; //internal linkage
              //non-static storage - each translation unit will have its own copy of x
              //NOT A TRUE GLOBAL!

int y;        //static storage duration (can be used with extern)
              //actual global
              //external linkage
struct X
{
   static int x;     //static storage duration - shared between class instances 
};

void foo()
{
   static int x;     //static storage duration - shared between calls
}

คำหลักคงที่ทั้งหมดนี้ทำให้เกิดความสับสนอย่างจริงจัง

แน่นอนเว้นแต่คุณจะคุ้นเคยกับมัน :) พยายามหลีกเลี่ยงการเพิ่มคำหลักใหม่ในภาษาคณะกรรมการนำมาใช้ใหม่ IMO นี้เพื่อผลนี้ - ความสับสน มันใช้เพื่อแสดงถึงสิ่งต่าง ๆ (ฉันอาจจะพูดว่าอาจจะเป็นสิ่งที่ตรงกันข้าม)


1
ให้ฉันได้สิ่งนี้โดยตรง - คุณกำลังบอกว่าเมื่อฉันพูดstatic int xที่ขอบเขตเนมสเปซมันให้ที่เก็บแบบไม่คงที่
Michael Hagar

31

เพื่อชี้แจงคำถามฉันต้องการจัดหมวดหมู่การใช้คำหลัก 'คงที่' ในรูปแบบที่แตกต่างกันสามแบบ:

(A) ตัวแปร

(B) ฟังก์ชั่น

(ค). ตัวแปรสมาชิก / ฟังก์ชั่นของการเรียน

คำอธิบายด้านล่างสำหรับแต่ละหัวข้อย่อย:

(A) คำหลัก 'คงที่' สำหรับตัวแปร

อันนี้อาจจะยุ่งยากเล็กน้อย แต่ถ้าอธิบายและเข้าใจอย่างถูกต้องมันค่อนข้างตรงไปตรงมา

เพื่ออธิบายสิ่งนี้สิ่งแรกคือมีประโยชน์จริง ๆ ที่จะรู้เกี่ยวกับขอบเขตระยะเวลาและการเชื่อมโยงของตัวแปรโดยที่สิ่งต่าง ๆ มักจะยากที่จะมองผ่านแนวคิดที่มืดมนของคำหลักที่เก่าแก่

1. ขอบเขต : กำหนดตำแหน่งที่อยู่ในไฟล์ตัวแปรสามารถเข้าถึงได้ มันอาจจะเป็นสองประเภท: (i) ท้องถิ่นหรือปิดกั้นขอบเขต (ii) ขอบเขตทั่วโลก

2. ระยะเวลา : กำหนดว่าเมื่อใดที่ตัวแปรถูกสร้างและทำลาย อีกครั้งเป็นสองประเภท: (i) Automatic Storage Duration (สำหรับตัวแปรที่มีขอบเขต Local หรือ Block) (ii) Static Storage Duration (สำหรับตัวแปรที่มีขอบเขตทั่วโลกหรือตัวแปรท้องถิ่น (ในฟังก์ชั่นหรือ a ในบล็อกรหัส) พร้อมตัวระบุแบบคงที่)

3. การเชื่อมโยง : กำหนดว่าตัวแปรสามารถเข้าถึงได้ (หรือเชื่อมโยง) ในไฟล์อื่น อีกครั้ง (และโชคดี) เป็นสองประเภท: (i) การเชื่อมโยงภายใน (สำหรับตัวแปรที่มีขอบเขตการบล็อกและขอบเขต / ขอบเขตไฟล์ทั่วโลก / ขอบเขตเนมสเปซส่วนกลาง) (ii) การเชื่อมโยงภายนอก (สำหรับตัวแปรที่มีเฉพาะขอบเขตสากล / ขอบเขตไฟล์ / ขอบเขตเนมสเปซส่วนกลาง)

ลองอ้างอิงตัวอย่างด้านล่างเพื่อทำความเข้าใจกับตัวแปรโกลบอลและโลคัลที่ดีขึ้น (ไม่มีตัวแปรโลคัลที่มีระยะเวลาเก็บแบบคงที่):

//main file
#include <iostream>

int global_var1; //has global scope
const global_var2(1.618); //has global scope

int main()
{
//these variables are local to the block main.
//they have automatic duration, i.e, they are created when the main() is 
//  executed and destroyed, when main goes out of scope
 int local_var1(23);
 const double local_var2(3.14);

 {
/* this is yet another block, all variables declared within this block are 
 have local scope limited within this block. */
// all variables declared within this block too have automatic duration, i.e, 
/*they are created at the point of definition within this block,
 and destroyed as soon as this block ends */
   char block_char1;
   int local_var1(32) //NOTE: this has been re-declared within the block, 
//it shadows the local_var1 declared outside

 std::cout << local_var1 <<"\n"; //prints 32

  }//end of block
  //local_var1 declared inside goes out of scope

 std::cout << local_var1 << "\n"; //prints 23

 global_var1 = 29; //global_var1 has been declared outside main (global scope)
 std::cout << global_var1 << "\n"; //prints 29
 std::cout << global_var2 << "\n"; //prints 1.618

 return 0;
}  //local_var1, local_var2 go out of scope as main ends
//global_var1, global_var2 go out of scope as the program terminates 
//(in this case program ends with end of main, so both local and global
//variable go out of scope together

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

การเชื่อมโยงของตัวแปรทั่วโลกมีการระบุโดยคำหลัก: (i) คงที่และ (ii) ภายนอก

(ตอนนี้คุณจะได้คำอธิบาย)

คำหลักคงที่สามารถนำไปใช้กับตัวแปรที่มีขอบเขตท้องถิ่นและทั่วโลกและในทั้งสองกรณีพวกเขาหมายถึงสิ่งที่แตกต่างกัน ฉันจะอธิบายการใช้คำหลัก 'คงที่' ในตัวแปรที่มีขอบเขตทั่วโลก (ซึ่งฉันจะอธิบายการใช้คำหลัก 'ภายนอก' ด้วย) และหลังจากนั้นสำหรับคำหลักที่มีขอบเขตในท้องถิ่น

1. คำหลักคงที่สำหรับตัวแปรที่มีขอบเขตทั่วโลก

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

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

ในทางตรงกันข้ามหากตัวแปรโกลบอลมีคำหลักคงที่จะไม่สามารถใช้ไฟล์นั้นนอกไฟล์ที่ได้รับการประกาศ

(ดูตัวอย่างด้านล่างเพื่อความกระจ่าง)

เช่น:

//main2.cpp
 static int global_var3 = 23;  /*static global variable, cannot be                            
                                accessed in anyother file */
 extern double global_var4 = 71; /*can be accessed outside this file                  linked to main2.cpp */
 int main() { return 0; }

main3.cpp

//main3.cpp
#include <iostream>

int main()
{
   extern int gloabl_var4; /*this variable refers to the gloabal_var4
                            defined in the main2.cpp file */
  std::cout << global_var4 << "\n"; //prints 71;

  return 0;
}

ตอนนี้ตัวแปรใด ๆ ใน c ++ สามารถเป็น const หรือ non-const และสำหรับแต่ละ 'const-ness' เราจะได้รับสองกรณีของการเชื่อมโยง c ++ เริ่มต้นในกรณีที่ไม่มีการระบุ:

(i) หากตัวแปรโกลบอลไม่ใช่แบบ const การเชื่อมโยงของมันจะเป็นค่าเริ่มต้นเช่นตัวแปรแบบโกลบอลที่ไม่ใช่ const สามารถเข้าถึงได้ในไฟล์. cpp อื่นโดยการประกาศไปข้างหน้าโดยใช้คำสำคัญ extern (ในคำอื่น ๆ ไม่ใช่ const global ตัวแปรมีการเชื่อมโยงภายนอก (ที่มีระยะเวลาคงที่แน่นอน)) การใช้คำหลัก extern ในไฟล์ต้นฉบับที่มีการกำหนดนั้นซ้ำซ้อน ในกรณีนี้จะทำให้ไม่ใช่ const ไม่สามารถเข้าถึงตัวแปรทั่วโลกไปยังแฟ้มภายนอกใช้ระบุว่า 'คงที่' ก่อนที่ประเภทของตัวแปร

(ii) ถ้าตัวแปรโกลบอลเป็น const การเชื่อมโยงของมันจะเป็นค่าคงที่โดยค่าเริ่มต้นเช่นตัวแปรโกลบอล const ไม่สามารถเข้าถึงได้ในไฟล์อื่นนอกเหนือจากที่มันถูกกำหนดไว้ (กล่าวอีกนัยหนึ่งว่าตัวแปรโกลบอลของ const มีการเชื่อมโยงภายใน แน่นอน)). การใช้คำหลักแบบสแตติกเพื่อป้องกันไม่ให้ตัวแปรโกลบอล const ถูกเข้าถึงในไฟล์อื่นนั้นซ้ำซ้อน ในที่นี้เพื่อให้ตัวแปรโกลบอล const มีการเชื่อมโยงภายนอกให้ใช้ตัวระบุ 'extern' ก่อนชนิดของตัวแปร

นี่คือบทสรุปสำหรับตัวแปรขอบเขตทั่วโลกที่มีการเชื่อมโยงที่หลากหลาย

//globalVariables1.cpp 

// defining uninitialized vairbles
int globalVar1; //  uninitialized global variable with external linkage 
static int globalVar2; // uninitialized global variable with internal linkage
const int globalVar3; // error, since const variables must be initialized upon declaration
const int globalVar4 = 23; //correct, but with static linkage (cannot be accessed outside the file where it has been declared*/
extern const double globalVar5 = 1.57; //this const variable ca be accessed outside the file where it has been declared

ต่อไปเราจะตรวจสอบว่าตัวแปรทั่วโลกทำงานอย่างไรเมื่อเข้าถึงในไฟล์อื่น

//using_globalVariables1.cpp (eg for the usage of global variables above)

// Forward declaration via extern keyword:
 extern int globalVar1; // correct since globalVar1 is not a const or static
 extern int globalVar2; //incorrect since globalVar2 has internal linkage
 extern const int globalVar4; /* incorrect since globalVar4 has no extern 
                         specifier, limited to internal linkage by
                         default (static specifier for const variables) */
 extern const double globalVar5; /*correct since in the previous file, it 
                           has extern specifier, no need to initialize the
                       const variable here, since it has already been
                       legitimately defined perviously */

2. คำหลักคงที่สำหรับตัวแปรที่มีขอบเขตในเครื่อง

อัปเดต (สิงหาคม 2019) กับคำหลักคงที่สำหรับตัวแปรในขอบเขตท้องถิ่น

นอกจากนี้ยังสามารถแบ่งย่อยในสองประเภท:

(i) คำหลักคงที่สำหรับตัวแปรภายในบล็อกฟังก์ชั่นและ(ii) คำหลักคงที่สำหรับตัวแปรภายในบล็อกท้องถิ่นที่ไม่มีชื่อ

(i) คำหลักคงที่สำหรับตัวแปรภายในบล็อกฟังก์ชั่น

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

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

ลองมาดูตัวอย่าง

//localVarDemo1.cpp    
 int localNextID()
{
  int tempID = 1;  //tempID created here
  return tempID++; //copy of tempID returned and tempID incremented to 2
} //tempID destroyed here, hence value of tempID lost

int newNextID()
{
  static int newID = 0;//newID has static duration, with internal linkage
  return newID++; //copy of newID returned and newID incremented by 1
}  //newID doesn't get destroyed here :-)


int main()
{
  int employeeID1 = localNextID();  //employeeID1 = 1
  int employeeID2 = localNextID();  // employeeID2 = 1 again (not desired)
  int employeeID3 = newNextID(); //employeeID3 = 0;
  int employeeID4 = newNextID(); //employeeID4 = 1;
  int employeeID5 = newNextID(); //employeeID5 = 2;
  return 0;
}

เมื่อมองจากเกณฑ์ข้างต้นสำหรับตัวแปรสแตติกท้องถิ่นและตัวแปรสแตติกทั่วโลกเราอาจถูกล่อลวงให้ถามว่าอะไรคือความแตกต่างระหว่างพวกเขา ในขณะที่ตัวแปรทั่วโลกสามารถเข้าถึงได้ที่จุดใด ๆ ภายในรหัส (ในเช่นเดียวกับหน่วยการแปลที่แตกต่างกันขึ้นอยู่กับconst -ness และextern -ness) ตัวแปรคงที่กำหนดภายในบล็อกฟังก์ชั่นไม่สามารถเข้าถึงได้โดยตรง ต้องส่งคืนตัวแปรโดยค่าฟังก์ชันหรือการอ้างอิง ให้แสดงตัวอย่างนี้โดย:

//localVarDemo2.cpp 

//static storage duration with global scope 
//note this variable can be accessed from outside the file
//in a different compilation unit by using `extern` specifier
//which might not be desirable for certain use case.
static int globalId = 0;

int newNextID()
{
  static int newID = 0;//newID has static duration, with internal linkage
  return newID++; //copy of newID returned and newID incremented by 1
}  //newID doesn't get destroyed here


int main()
{
    //since globalId is accessible we use it directly
  const int globalEmployee1Id = globalId++; //globalEmployeeId1 = 0;
  const int globalEmployee2Id = globalId++; //globalEmployeeId1 = 1;

  //const int employeeID1 = newID++; //this will lead to compilation error since newID++ is not accessible direcly. 
  int employeeID2 = newNextID(); //employeeID3 = 0;
  int employeeID2 = newNextID(); //employeeID3 = 1;

  return 0;
}

คำอธิบายเพิ่มเติมเกี่ยวกับตัวเลือกของตัวแปรโลคัลโกลบอลและสแตติกแบบคงที่สามารถพบได้บนเธรดสแต็ก

(ii) คำหลักคงที่สำหรับตัวแปรภายในบล็อกโลคัลที่ไม่มีชื่อ

ตัวแปรสแตติกภายในบล็อกโลคัล (ไม่ใช่บล็อกฟังก์ชัน) ไม่สามารถเข้าถึงได้นอกบล็อกเมื่อบล็อกโลคัลอยู่นอกขอบเขต ไม่มีข้อควรปฏิบัติสำหรับกฎนี้

    //localVarDemo3.cpp 
    int main()
    {

      {
          const static int static_local_scoped_variable {99};
      }//static_local_scoped_variable goes out of scope

      //the line below causes compilation error
      //do_something is an arbitrary function
      do_something(static_local_scoped_variable);
      return 0;
    }

C ++ 11 แนะนำคำสำคัญconstexprที่รับประกันการประเมินผลของการแสดงออกในเวลารวบรวมและช่วยให้คอมไพเลอร์เพื่อเพิ่มประสิทธิภาพรหัส ตอนนี้ถ้าค่าของตัวแปร const constexprคงอยู่ในขอบเขตที่เป็นที่รู้จักกันที่รวบรวมเวลารหัสมีการเพิ่มประสิทธิภาพในลักษณะที่คล้ายคลึงกับหนึ่งเดียวกับที่ นี่คือตัวอย่างเล็ก ๆ

ฉันขอแนะนำให้ผู้อ่านเพื่อค้นหาความแตกต่างระหว่างconstexprและstatic constสำหรับตัวแปรในเธรด stackoverflowนี้ นี้สรุปคำอธิบายของฉันสำหรับคำหลักคงที่นำไปใช้กับตัวแปร

คำหลัก B. 'คงที่' ใช้สำหรับฟังก์ชั่น

ในแง่ของฟังก์ชั่นคำหลักคงที่มีความหมายตรงไปตรงมา นี่มันหมายถึงการเชื่อมโยงของฟังก์ชั่น โดยปกติฟังก์ชั่นทั้งหมดที่ประกาศภายในไฟล์ cpp มีการเชื่อมโยงภายนอกโดยค่าเริ่มต้นคือฟังก์ชั่นที่กำหนดไว้ในไฟล์หนึ่งสามารถใช้ในไฟล์ cpp อื่นโดยการประกาศไปข้างหน้า

ใช้คำสำคัญคงที่ก่อนประกาศฟังก์ชั่น จำกัด การเชื่อมโยงไปยังภายในเช่นฟังก์ชั่นคงที่ไม่สามารถใช้ภายในไฟล์นอกคำจำกัดความของมัน

C. Staitc Keyword ใช้สำหรับตัวแปรสมาชิกและฟังก์ชั่นของคลาส

1. คำหลัก 'คงที่' สำหรับตัวแปรสมาชิกของคลาส

ฉันเริ่มต้นโดยตรงจากตัวอย่างที่นี่

#include <iostream>

class DesignNumber
{
  private:

      static int m_designNum;  //design number
      int m_iteration;     // number of iterations performed for the design

  public:
    DesignNumber() {     }  //default constructor

   int  getItrNum() //get the iteration number of design
   {
      m_iteration = m_designNum++;
      return m_iteration;
   }
     static int m_anyNumber;  //public static variable
};
int DesignNumber::m_designNum = 0; // starting with design id = 0
                     // note : no need of static keyword here
                     //causes compiler error if static keyword used
int DesignNumber::m_anyNumber = 99; /* initialization of inclass public 
                                    static member  */
enter code here

int main()
{
   DesignNumber firstDesign, secondDesign, thirdDesign;
   std::cout << firstDesign.getItrNum() << "\n";  //prints 0
   std::cout << secondDesign.getItrNum() << "\n"; //prints 1
   std::cout << thirdDesign.getItrNum() << "\n";  //prints 2

   std::cout << DesignNumber::m_anyNumber++ << "\n";  /* no object
                                        associated with m_anyNumber */
   std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 100
   std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 101

   return 0;
}

ในตัวอย่างนี้ตัวแปรแบบคงที่ m_designNum จะเก็บค่าไว้และตัวแปรสมาชิกส่วนบุคคลเดียว (เพราะเป็นแบบคงที่) จะถูกแชร์ b / w ตัวแปรทั้งหมดของประเภทวัตถุ DesignNumber

เช่นเดียวกับตัวแปรสมาชิกอื่น ๆ ตัวแปรสมาชิกแบบคงที่ของคลาสไม่เกี่ยวข้องกับคลาสอ็อบเจ็กต์ใด ๆ ซึ่งแสดงให้เห็นโดยการพิมพ์ anyNumber ในฟังก์ชันหลัก

const vs ตัวแปรสมาชิกคงไม่ใช่ const ในชั้นเรียน

(i) ตัวแปรสมาชิกแบบคงที่ที่ไม่ใช่สมาชิกคลาส ในตัวอย่างก่อนหน้าสมาชิกแบบคงที่ (ทั้งสาธารณะและส่วนตัว) ไม่ใช่ค่าคงที่ มาตรฐาน ISO ห้ามสมาชิกแบบคงที่ที่ไม่ใช่สมาชิกที่จะเริ่มต้นในชั้นเรียน ดังนั้นในตัวอย่างก่อนหน้านี้พวกเขาจะต้อง initalized หลังจากที่คำจำกัดความของชั้นเรียนโดยมีข้อแม้ที่คำหลักคงที่จะต้องละเว้น

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

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

สำหรับตัวอย่างเพิ่มเติมเกี่ยวกับตัวแปรสมาชิกแบบคงที่ในชั้นเรียนค้นหาลิงก์ต่อไปนี้จาก learncpp.com http://www.learncpp.com/cpp-tutorial/811-static-member-variables/

2. คีย์เวิร์ด 'static' สำหรับฟังก์ชันสมาชิกของคลาส

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

ประการที่สองเนื่องจากฟังก์ชั่นสมาชิกคงที่ของชั้นเรียนไม่มี * ตัวชี้นี้พวกเขาสามารถเรียกใช้ชื่อคลาสและผู้ประกอบการแก้ไขขอบเขตในฟังก์ชั่นหลัก (ClassName :: functionName ();)

ฟังก์ชันสมาชิกแบบสแตติกที่สามของคลาสสามารถเข้าถึงตัวแปรสมาชิกแบบคงที่ของคลาสเท่านั้นเนื่องจากตัวแปรสมาชิกแบบไม่คงที่ของคลาสจะต้องเป็นของวัตถุคลาส

สำหรับตัวอย่างเพิ่มเติมเกี่ยวกับฟังก์ชั่นสมาชิกคงที่ในชั้นเรียนค้นหาลิงค์ต่อไปนี้จาก learncpp.com

http://www.learncpp.com/cpp-tutorial/812-static-member-functions/


1
1) ก่อนที่ c ++ 17 จะมีเพียงสมาชิกสแตติกแบบคงที่หนึ่งเท่านั้นที่สามารถกำหนดค่าเริ่มต้นในคลาสได้เช่นstruct Foo{static const std::string name = "cpp";};ข้อผิดพลาดnameต้องถูกกำหนดนอกคลาส ด้วยตัวแปรอินไลน์ intruduced ใน c ++ 17 หนึ่งสามารถรหัส: struct Foo{static inline const std::string name = "cpp";};2) ฟังก์ชั่นสมาชิกคงที่ / สมาชิกสาธารณะสามารถเข้าถึงได้โดยชื่อชั้นที่มีการดำเนินการแก้ไขขอบเขตและยังมีอินสแตนซ์ที่มีตัวดำเนินการจุด (เช่น: instance.some_static_method ())
oz1

ไม่ควร "m_anyVariable" กลายเป็น "m_anyNumber" ในตัวอย่างรหัสสุดท้ายของคุณ?
gebbissimo

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

18

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

int myFun()
{
static int i=5;
i++;
return i;
}
int main()
{
printf("%d", myFun());
printf("%d", myFun());
printf("%d", myFun());
}

จะแสดง678แทน666เพราะจะจำค่าที่เพิ่มขึ้น

สำหรับสมาชิกแบบคงที่พวกเขารักษาคุณค่าของพวกเขาในกรณีของชั้นเรียน ดังนั้นรหัสต่อไปนี้:

struct A
{
static int a;
};
int main()
{
A first;
A second;
first.a = 3;
second.a = 4;
printf("%d", first.a);
}

จะพิมพ์ 4 เพราะ first.a และ second.a นั้นเป็นตัวแปรเดียวกัน สำหรับการเริ่มต้นให้ดูคำถามนี้


สิ่งนี้ไม่ได้ระบุตัวแปรขอบเขตเนมสเปซ
Michael Hagar

10

เมื่อคุณประกาศstaticตัวแปรในขอบเขตไฟล์แล้วว่าตัวแปรจะใช้ได้เฉพาะในที่แฟ้มเฉพาะ (เทคนิคหน่วยแปล * แต่ขอไม่ซับซ้อนนี้มากเกินไป) ตัวอย่างเช่น:

a.cpp

static int x = 7;

void printax()
{
    cout << "from a.cpp: x=" << x << endl;
}

b.cpp

static int x = 9;

void printbx()
{
    cout << "from b.cpp: x=" << x << endl;
}

main.cpp:

int main(int, char **)
{
    printax(); // Will print 7
    printbx(); // Will print 9

    return 0;
}

สำหรับ ตัวแปรโลคัลstaticหมายความว่าตัวแปรจะถูกกำหนดค่าเริ่มต้นเป็นศูนย์และเก็บค่าไว้ระหว่างการโทร:

unsigned int powersoftwo()
{
    static unsigned lastpow;

    if(lastpow == 0)
        lastpow = 1;
    else
        lastpow *= 2;

    return lastpow;
}

int main(int, char **)
{
    for(int i = 0; i != 10; i++)
        cout << "2^" << i << " = " << powersoftwo() << endl;
}

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

class Test
{
private:
    static char *xxx;

public:
    static int yyy;

public:
    Test()
    {        
        cout << this << "The static class variable xxx is at address "
             << static_cast<void *>(xxx) << endl;
        cout << this << "The static class variable yyy is at address "
             << static_cast<void *>(&y) << endl;
    }
};

// Necessary for static class variables.
char *Test::xxx = "I'm Triple X!";
int Test::yyy = 0;

int main(int, char **)
{
    Test t1;
    Test t2;

    Test::yyy = 666;

    Test t3;
};

การทำเครื่องหมายฟังก์ชั่นที่ไม่ใช่ระดับที่staticทำให้ฟังก์ชั่นสามารถเข้าถึงได้จากไฟล์นั้นและไม่สามารถเข้าถึงได้จากไฟล์อื่น

a.cpp

static void printfilename()
{ // this is the printfilename from a.cpp - 
  // it can't be accessed from any other file
    cout << "this is a.cpp" << endl;
}

b.cpp

static void printfilename()
{ // this is the printfilename from b.cpp - 
  // it can't be accessed from any other file
    cout << "this is b.cpp" << endl;
}

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

class Test
{
private:
    static int count;

public:
    static int GetTestCount()
    {
        return count;
    };

    Test()
    {
        cout << this << "Created an instance of Test" << endl;
        count++;
    }

    ~Test()
    {
        cout << this << "Destroyed an instance of Test" << endl;
        count--;
    }
};

int Test::count = 0;

int main(int, char **)
{
    Test *arr[10] = { NULL };

    for(int i = 0; i != 10; i++)
        arr[i] = new Test();

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    // now, delete them all except the first and last!
    for(int i = 1; i != 9; i++)
        delete arr[i];        

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    delete arr[0];

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    delete arr[9];

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    return 0;
}

8

ตัวแปรสแตติกใช้ร่วมกันระหว่างทุกอินสแตนซ์ของคลาสแทนแต่ละคลาสที่มีตัวแปรของตัวเอง

class MyClass
{
    public:
    int myVar; 
    static int myStaticVar;
};

//Static member variables must be initialized. Unless you're using C++11, or it's an integer type,
//they have to be defined and initialized outside of the class like this:
MyClass::myStaticVar = 0;

MyClass classA;
MyClass classB;

แต่ละอินสแตนซ์ของ 'MyClass' มี 'myVar' ของตัวเอง แต่แชร์ 'myStaticVar' เดียวกัน ในความเป็นจริงคุณไม่จำเป็นต้องมีอินสแตนซ์ของ MyClass เพื่อเข้าถึง 'myStaticVar' และคุณสามารถเข้าถึงได้นอกคลาสเช่นนี้:

MyClass::myStaticVar //Assuming it's publicly accessible.

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

int myFunc()
{
   int myVar = 0; //Each time the code reaches here, a new variable called 'myVar' is initialized.
   myVar++;

   //Given the above code, this will *always* print '1'.
   std::cout << myVar << std::endl;

   //The first time the code reaches here, 'myStaticVar' is initialized. But ONLY the first time.
   static int myStaticVar = 0;

   //Each time the code reaches here, myStaticVar is incremented.
   myStaticVar++;

   //This will print a continuously incrementing number,
   //each time the function is called. '1', '2', '3', etc...
   std::cout << myStaticVar << std::endl;
}

มันเป็นตัวแปรระดับโลกในแง่ของการคงอยู่ ... แต่ไม่มีขอบเขตระดับโลกในการเข้าถึง /

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

class MyClass
{
    public:
    int Func()
    {
        //...do something...
    }

    static int StaticFunc()
    {
        //...do something...
    }
};

int main()
{
   MyClass myClassA;
   myClassA.Func(); //Calls 'Func'.
   myClassA.StaticFunc(); //Calls 'StaticFunc'.

   MyClass::StaticFunc(); //Calls 'StaticFunc'.
   MyClass::Func(); //Error: You can't call a non-static member-function without a class instance!

   return 0;
}

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


1
"สมมติว่าสาธารณชนสามารถเข้าถึงได้" - มันไม่ใช่.
Luchian Grigore

2
myStaticVarจะต้องมีการกำหนดยัง ค่อนข้างสำคัญที่จะพูดถึงว่าเมื่อตอบคำถามเกี่ยวกับความหมายของstaticคำหลักคุณไม่คิดอย่างนั้นหรือ
Praetorian

@ Praetorian: ขอบคุณแก้ไขแล้ว
Jamin Grey

1
@JaminGrey โดย "static standalone" ฉันหมายถึงฟังก์ชั่นที่ไม่ใช่สมาชิกและฉันเขียนเมื่อใดก็ตามที่ฉันต้องการฟังก์ชั่นใหม่บางอย่างเฉพาะในไฟล์ CPP ปัจจุบันและไม่ต้องการให้ linker ต้องประมวลผลสัญลักษณ์เพิ่มเติม
VR

1
@VR แปลก! ฉันไม่เคยรู้ว่ามีฟังก์ชั่นอยู่ ขอบคุณสำหรับการขยายความรู้ของฉัน!
Jamin Grey

1

ฉันไม่ใช่โปรแกรมเมอร์ C ดังนั้นฉันจึงไม่สามารถให้ข้อมูลเกี่ยวกับการใช้งานสแตติกในโปรแกรม C ได้อย่างเหมาะสม แต่เมื่อพูดถึงการเขียนโปรแกรม Object Oriented คงที่โดยทั่วไปประกาศตัวแปรหรือฟังก์ชั่นหรือคลาสให้เหมือนกัน ตลอดชีวิตของโปรแกรม ยกตัวอย่าง

class A
{
public:
    A();
    ~A();
    void somePublicMethod();
private:
    void somePrivateMethod();
};

เมื่อคุณยกตัวอย่างคลาสนี้ใน Main ของคุณคุณจะทำสิ่งนี้

int main()
{
   A a1;
   //do something on a1
   A a2;
   //do something on a2
}

อินสแตนซ์ของคลาสสองเหล่านี้แตกต่างจากกันโดยสิ้นเชิงและดำเนินการอย่างเป็นอิสระจากกัน แต่ถ้าคุณจะสร้างคลาส A แบบนี้ขึ้นมาใหม่

class A
{
public:
    A();
    ~A();
    void somePublicMethod();
    static int x;
private:
    void somePrivateMethod();
};

กลับไปที่หน้าหลักอีกครั้ง

int main()
{
   A a1;
   a1.x = 1;
   //do something on a1
   A a2;
   a2.x++;
   //do something on a2
}

จากนั้น a1 และ a2 จะแบ่งปันสำเนา int x เดียวกันโดยที่การดำเนินการใด ๆ กับ x ใน a1 จะส่งผลโดยตรงต่อการทำงานของ x ใน a2 ดังนั้นถ้าฉันจะทำเช่นนี้

int main()
{
   A a1;
   a1.x = 1;
   //do something on a1
   cout << a1.x << endl; //this would be 1
   A a2;
   a2.x++;
   cout << a2.x << endl; //this would be 2 
   //do something on a2
}

อินสแตนซ์ของคลาส A แบ่งใช้ตัวแปรและฟังก์ชันแบบคงที่ หวังว่านี่จะตอบคำถามของคุณ ความรู้ที่ จำกัด ของฉันเกี่ยวกับ C ทำให้ฉันสามารถบอกได้ว่าการกำหนดฟังก์ชั่นหรือตัวแปรเป็นแบบคงที่หมายถึงมันจะปรากฏเฉพาะกับไฟล์ที่ฟังก์ชั่นหรือตัวแปรถูกกำหนดเป็นแบบคงที่ แต่สิ่งนี้จะตอบได้ดีกว่า C ++ อนุญาตให้ทั้ง C และ C ++ มีวิธีการประกาศตัวแปรของคุณเป็นแบบคงที่เพราะมันเข้ากันได้กับ C ย้อนหลังอย่างสมบูรณ์


1

มันหมายความว่าอะไรกับตัวแปรท้องถิ่น? นั่นคือฟังก์ชั่นตัวแปรท้องถิ่น?

ใช่ - ไม่ใช่ทั่วโลกเช่นฟังก์ชั่นตัวแปรท้องถิ่น

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

ขวา.

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

class R { static int a; }; // << static lives for the duration of the program

กล่าวคือทุกกรณีของการRแบ่งปันint R::a- int R::aจะไม่ถูกคัดลอก

ตอนนี้สิ่งที่เกี่ยวกับกรณีที่มีขอบเขตคงที่และไฟล์?

อย่างมีประสิทธิภาพระดับโลกที่มีคอนสตรัคเตอร์ / destructor ที่เหมาะสม - การเริ่มต้นจะไม่รอจนกระทั่งการเข้าถึง

คงที่เกี่ยวข้องกับการเชื่อมโยงของตัวแปรอย่างไร

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

สำหรับชั้นเรียนมันเป็นภายนอก การเข้าถึง: ใช้ตัวระบุการเข้าถึงมาตรฐาน (สาธารณะการป้องกันส่วนตัว)

static ยังสามารถระบุการเชื่อมโยงภายในขึ้นอยู่กับตำแหน่งที่ประกาศ (ไฟล์ / เนมสเปซ)

คำหลักคงที่ทั้งหมดนี้ทำให้เกิดความสับสนอย่างจริงจัง

มันมีวัตถุประสงค์มากเกินไปใน C ++

บางคนสามารถอธิบายการใช้ภาษาอังกฤษที่แตกต่างกันและบอกฉันเมื่อใดที่จะเริ่มต้นสมาชิกคลาสแบบคงที่?

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


1

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

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

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

#include <iostream>

using namespace std;

class Box
{
   public:
      static int objectCount;
      // Constructor definition
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // Increase every time object is created
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

// Initialize static member of class Box
int Box::objectCount = 0;

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   // Print total number of objects.
   cout << "Total objects: " << Box::objectCount << endl;

   return 0;
}

เมื่อโค้ดข้างต้นรวบรวมและดำเนินการมันจะสร้างผลลัพธ์ดังต่อไปนี้:

Constructor called.
Constructor called.
Total objects: 2

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

ฟังก์ชั่นสมาชิกคงที่สามารถเข้าถึงสมาชิกข้อมูลคงที่ฟังก์ชั่นสมาชิกคงที่อื่น ๆ และฟังก์ชั่นอื่น ๆ จากนอกชั้นเรียน

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

ให้เราลองตัวอย่างต่อไปนี้เพื่อทำความเข้าใจแนวคิดของสมาชิกฟังก์ชันคงที่:

#include <iostream>

using namespace std;

class Box
{
   public:
      static int objectCount;
      // Constructor definition
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // Increase every time object is created
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      static int getCount()
      {
         return objectCount;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

// Initialize static member of class Box
int Box::objectCount = 0;

int main(void)
{

   // Print total number of objects before creating object.
   cout << "Inital Stage Count: " << Box::getCount() << endl;

   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   // Print total number of objects after creating object.
   cout << "Final Stage Count: " << Box::getCount() << endl;

   return 0;
}

เมื่อโค้ดข้างต้นรวบรวมและดำเนินการมันจะสร้างผลลัพธ์ดังต่อไปนี้:

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2

1
มันจะเป็นสิทธิที่จะพูดถึงว่ากระบวนทัศน์นี้ถูกนำมาจากtutorialspoint.com/cplusplus/cpp_static_members.htm
BugShotGG
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.