ความหมายของเครื่องหมายทวิภาคคู่แบบ“ prepended” คืออะไร?


410

ฉันพบบรรทัดของรหัสนี้ในคลาสที่ฉันต้องแก้ไข:

::Configuration * tmpCo = m_configurationDB;//pointer to current db

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

ฉันก็ค้นพบ:

typedef ::config::set ConfigSet;

7
ไม่ได้จริงๆรู้สึกว่ามันเป็นคำตอบดังนั้นฉันจะแสดงความคิดเห็น: en.wikipedia.org/wiki/Scope_resolution_operator ในบริบทนี้ naked ::หมายถึงการอ้างอิงตัวแปรจาก namespace ส่วนกลาง / ไม่ระบุชื่อ
wkl

คำตอบ:


490

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

class Configuration; // class 1, in global namespace
namespace MyApp
{
    class Configuration; // class 2, different from class 1
    function blah()
    {
        // resolves to MyApp::Configuration, class 2
        Configuration::doStuff(...) 
        // resolves to top-level Configuration, class 1
        ::Configuration::doStuff(...)
    }
}

โดยทั่วไปจะช่วยให้คุณสามารถข้ามไปที่เนมสเปซส่วนกลางได้เนื่องจากชื่อของคุณอาจถูกปกปิดโดยนิยามใหม่ภายในเนมสเปซอื่นในกรณีMyAppนี้


อะไรคือสาเหตุที่ทำให้มีสองชุดโคลอนคู่? ในนี้:::Configuration::doStuff(...)
Azurespot

@NoniA คุณกำลังถามว่าทวิภาคชุดที่สองทำอะไรได้บ้าง
FCo

1
@ ไวแอตแอนเดอร์สันไม่มีชุดที่ 1 ฉันคิดว่าฉันเข้าใจว่า::ในระหว่างคำสองคำนั้นหมายถึงเนมสเปซหรือคลาสและสมาชิก แต่แล้วอันที่หนึ่งล่ะ?
Azurespot

6
@Azurespot นั่นคือสิ่งที่ OP ถามนั่นคือคำถามที่คำตอบโพสต์นี้ มันทำให้แน่ใจว่าใช้ตัวระบุจากเนมสเปซส่วนกลาง ดูตัวอย่างอีกครั้ง
hungryWolf

193

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

ตัวอย่าง:

int count = 0;

int main(void) {
  int count = 0;
  ::count = 1;  // set global count to 1
  count = 2;    // set local count to 2
  return 0;
}

122

มีคำตอบที่สมเหตุสมผลมากมายแล้ว ฉันจะชิปด้วยการเปรียบเทียบที่อาจช่วยให้ผู้อ่านบางคน ::ทำงานได้มากเช่นตัวคั่นไดเรกทอรีระบบแฟ้ม ' /' เมื่อค้นหาเส้นทางของคุณเพื่อหาโปรแกรมที่คุณต้องการเรียกใช้ พิจารณา:

/path/to/executable

สิ่งนี้ชัดเจนมาก - เฉพาะปฏิบัติการที่ตำแหน่งที่แน่นอนในทรีของระบบแฟ้มเท่านั้นที่สามารถจับคู่ข้อมูลจำเพาะนี้ได้โดยไม่คำนึงถึง PATH ที่มีผลบังคับใช้ ในทำนองเดียวกัน ...

::std::cout

... มีความชัดเจนเท่าเทียมกันใน "ต้นไม้" ของ C ++ เนมสเปซ

เมื่อเปรียบเทียบกับพา ธ สัมบูรณ์เช่นนั้นคุณสามารถกำหนดค่าเชลล์ UNIX ที่ดี (เช่นzsh ) เพื่อแก้ไขพา ธสัมพัทธ์ภายใต้ไดเรกทอรีปัจจุบันของคุณหรือองค์ประกอบใด ๆ ในPATHตัวแปรสภาพแวดล้อมของคุณดังนั้นหากPATH=/usr/bin:/usr/local/binและคุณอยู่ "ใน" /tmpแล้ว ...

X11/xterm

... จะทำงานอย่างมีความสุข/tmp/X11/xtermหากพบอื่นอื่น/usr/bin/X11/xterm /usr/local/bin/X11/xtermในทำนองเดียวกันสมมติว่าคุณอยู่ในเนมสเปซที่เรียกว่าXและมีusing namespace Yผลบังคับใช้" " แล้ว ...

std::cout

... อาจจะพบในใด ๆ ของ::X::std::cout, ::std::cout, ::Y::std::coutและสถานที่อื่น ๆ อาจจะเนื่องมาจากการค้นหาโต้แย้งขึ้นอยู่กับ (ADL อาคานิกการค้นหา) ดังนั้นเพียง::std::coutเป็นจริงอย่างชัดเจนเกี่ยวกับว่าที่คุณหมายถึงวัตถุ แต่ไม่มีใครโชคดีที่ในใจขวาของพวกเขาเคยจะสร้างชั้นของตัวเอง / struct หรือ namespace ที่เรียกว่า " std" หรือสิ่งใด ๆ ที่เรียกว่า " cout" ดังนั้นในทางปฏิบัติโดยใช้เพียงstd::coutเป็นเรื่องปกติ

ความแตกต่างที่น่าสังเกต :

1) เชลล์มีแนวโน้มที่จะใช้คู่แรกโดยใช้การสั่งซื้อในPATHขณะที่ C ++ ให้ข้อผิดพลาดคอมไพเลอร์เมื่อคุณไม่ชัดเจน

2) ใน C ++ ชื่อโดยไม่ต้องขอบเขตชั้นนำใด ๆ สามารถจับคู่ใน namespace ปัจจุบันขณะที่ส่วนใหญ่เปลือกหอยยูนิกซ์เท่านั้นทำว่าถ้าคุณใส่ใน.PATH

3) C ++ จะค้นหาเนมสเปซส่วนกลาง (เช่นมี/ของคุณโดยปริยายPATH)

การสนทนาทั่วไปเกี่ยวกับ namespaces และชัดเจนของสัญลักษณ์

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

เช่นเดียวกับหลาย ๆ สิ่งมันเป็นการกระทำที่สมดุล c ++ มาตรฐานทำให้จำนวนมากของตัวระบุภายใต้std::ที่น้อย "พิเศษ" กว่าcoutที่โปรแกรมเมอร์อาจใช้สำหรับบางสิ่งบางอย่างที่แตกต่างกันอย่างสมบูรณ์ในรหัสของพวกเขา (เช่นmerge, includes, fill, generate, exchange, queue, toupper, max) สองไลบรารีที่ไม่เกี่ยวข้องที่ไม่ได้มาตรฐานนั้นมีโอกาสสูงกว่าที่จะใช้ตัวระบุเดียวกันกับผู้เขียนโดยทั่วไปแล้วจะไม่ได้ตระหนักถึงหรือน้อยกว่ากัน และไลบรารี - รวมถึงไลบรารีมาตรฐาน C ++ - เปลี่ยนสัญลักษณ์ของพวกเขาเมื่อเวลาผ่านไป ทั้งหมดนี้อาจสร้างความกำกวมเมื่อทำการคอมไพล์โค้ดเก่าโดยเฉพาะเมื่อมีการใช้งานจำนวนมากusing namespaces: สิ่งที่แย่ที่สุดที่คุณสามารถทำได้ในพื้นที่นี้คืออนุญาตusing namespaceในส่วนหัวเพื่อหลีกเลี่ยงขอบเขตของส่วนหัวเช่นว่ารหัสลูกค้าจำนวนมากทั้งทางตรงและทางอ้อมไม่สามารถตัดสินใจได้เองว่าควรใช้ namespaces ใดและวิธีการจัดการความคลุมเครือ

ดังนั้นสิ่งที่สำคัญที่สุด::คือเครื่องมือหนึ่งในกล่องเครื่องมือของโปรแกรมเมอร์ C + + ที่จะลดความขัดแย้งที่รู้จักกันอย่างแข็งขันและ / หรือกำจัดความเป็นไปได้ของความคลุมเครือในอนาคต ....


8
+1 สำหรับการเปรียบเทียบที่ดี การเปรียบเทียบไม่ได้ใช้ IMO เกือบเท่าเครื่องมือการสอน
เทรเวอร์บอยด์สมิ ธ

38

::เป็นผู้ดำเนินการแก้ไขปัญหาขอบเขต มันใช้เพื่อระบุขอบเขตของบางสิ่งบางอย่าง

ตัวอย่างเช่น::ลำพังเป็นขอบเขตโกลบอลนอกเนมสเปซอื่นทั้งหมด

some::thing สามารถตีความได้ด้วยวิธีใดวิธีหนึ่งต่อไปนี้:

  • someเป็นnamespace (ในขอบเขตทั่วโลกหรือขอบเขตด้านนอกกว่าหนึ่งในปัจจุบัน) และthingเป็นชนิดที่มีฟังก์ชั่นเป็นวัตถุหรือnamespace ซ้อนกัน ;
  • someเป็นระดับที่มีอยู่ในขอบเขตในปัจจุบันและthingเป็นวัตถุที่เป็นสมาชิก , ฟังก์ชั่นหรือประเภทของsomeระดับ;
  • ในฟังก์ชั่นสมาชิกระดับ , someสามารถเป็นพิมพ์ฐานของชนิดปัจจุบัน (หรือชนิดปัจจุบันเอง) และthingหลังจากนั้นก็เป็นหนึ่งในสมาชิกของชั้นนี้เป็นชนิด , ฟังก์ชั่นหรือวัตถุ

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

ดังนั้นกลับไปที่ตัวอย่างของคุณ::thingสามารถมีได้เฉพาะบางอย่างในขอบเขตส่วนกลาง: ประเภทฟังก์ชันวัตถุหรือเนมสเปซ

วิธีที่คุณใช้แนะนำ (ใช้ในการประกาศตัวชี้) ว่าเป็นประเภทในขอบเขตส่วนกลาง

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


2
@obounaim พิจารณารหัสนี้liveworkspace.org/code/3Wabw0$5 class some { protected: int thing; }; class some_ext : public some { float thing; void action(){ some::thing = 42; thing = 666; } }; นี่someเป็นชั้นฐานของsome_extและเมื่อคุณเขียนsome::thingลงในฟังก์ชั่นสมาชิกของ some_ext ก็หมายความว่าวัตถุลงในประเภทฐานthing someโดยไม่ต้องsome::, thingเพียงอย่างเดียวหมายถึงการอยู่ในขอบเขตที่ใกล้เคียงที่สุดนั่นคือthing some_ext::thingชัดเจนขึ้นหรือไม่
Klaim

17

:: ใช้เพื่อเชื่อมโยงบางสิ่ง (ตัวแปรฟังก์ชันคลาส typedef ฯลฯ ... ) ไปยังเนมสเปซหรือคลาส

หากไม่มีด้านซ้ายมือมาก่อน::มันจะขีดเส้นใต้ข้อเท็จจริงที่ว่าคุณกำลังใช้เนมสเปซส่วนกลาง

เช่น:

::doMyGlobalFunction();


10

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

int x;
void f2()
{
   int x = 1; // hide global x
   ::x = 2; // assign to global x
   x = 2; // assign to local x
   // ...
}

10

(คำตอบนี้ส่วนใหญ่สำหรับชาว Google เนื่องจาก OP ได้แก้ไขปัญหาของเขาไปแล้ว) ความหมายของคำว่า prepended ::- ตัวดำเนินการลดขอบเขต - ได้อธิบายไว้ในคำตอบอื่น ๆ แต่ฉันต้องการเพิ่มว่าทำไมผู้คนถึงใช้งาน

ความหมายคือ "ใช้ชื่อจากเนมสเปซส่วนกลางไม่ใช่อย่างอื่น" แต่ทำไมต้องสะกดอย่างชัดเจน

ใช้ case - clash เนมสเปซ

เมื่อคุณมีชื่อเดียวกันในเนมสเปซส่วนกลางและในเนมสเปซท้องถิ่น / ที่ซ้อนกันจะใช้ชื่อโลคัล ::ดังนั้นหากคุณต้องการคนทั่วโลกย่อหน้าด้วย กรณีนี้ถูกอธิบายในคำตอบของ @Wyatt Anderson โปรดดูตัวอย่างของเขา

ใช้กรณี - เน้นฟังก์ชั่นที่ไม่ใช่สมาชิก

เมื่อคุณกำลังเขียนฟังก์ชันสมาชิก (เมธอด) การเรียกไปยังฟังก์ชันสมาชิกอื่นและการโทรไปยังฟังก์ชันที่ไม่ใช่สมาชิก (ฟรี) มีลักษณะเหมือนกัน:

class A {
   void DoSomething() {
      m_counter=0;
      ...
      Twist(data); 
      ...
      Bend(data);
      ...
      if(m_counter>0) exit(0);
   }
   int m_couner;
   ...
}

แต่มันอาจเกิดขึ้นนั่นTwistคือฟังก์ชั่นสมาชิกของชั้นเรียนAและBendเป็นฟังก์ชั่นฟรี นั่นคือTwistสามารถใช้และแก้ไขm_counerและBendไม่สามารถ ดังนั้นหากคุณต้องการเพื่อให้มั่นใจว่าm_counterซาก 0 คุณต้องตรวจสอบแต่คุณไม่จำเป็นต้องตรวจสอบTwistBend

ดังนั้นเพื่อทำให้สิ่งนี้โดดเด่นยิ่งขึ้นเราสามารถเขียนthis->Twistเพื่อแสดงตัวอ่านที่Twistเป็นฟังก์ชันสมาชิกหรือเขียน::Bendเพื่อแสดงว่าBendเป็นอิสระ หรือทั้งคู่. สิ่งนี้มีประโยชน์มากเมื่อคุณกำลังทำหรือวางแผนการปรับโครงสร้างใหม่


5

:: เป็นผู้ดำเนินการกำหนด namespace

ตัวอย่างเช่นหากคุณต้องการใช้ cout โดยไม่พูดถึงusing namespace std;ในรหัสของคุณคุณเขียนสิ่งนี้:

std::cout << "test";

เมื่อไม่มีการกล่าวถึงเนมสเปซจะกล่าวได้ว่าคลาสนั้นเป็นของเนมสเปซส่วนกลาง


1

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

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