เมื่อใดที่ฉันจะใช้จุดลูกศรหรือเครื่องหมายโคลอนคู่เพื่ออ้างถึงสมาชิกของคลาสใน C ++


243

มาจากภาษา C ที่ได้มาจากอื่น ๆ (เช่น Java หรือ C #) กับ C ++ มันเป็นครั้งแรกที่ทำให้เกิดความสับสนมากว่า C ++ มีสามวิธีในการอ้างถึงสมาชิกของชั้นเรียน: a::b, และa.b a->bฉันจะใช้โอเปอเรเตอร์ตัวใด

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

คำตอบ:


248

ตัวดำเนินการ C ++ ที่แตกต่างกันสามตัวใช้เพื่อเข้าถึงสมาชิกของคลาสหรือวัตถุคลาส ได้แก่ โคลอนคู่::จุด.และลูกศร->ใช้สำหรับสถานการณ์ที่แตกต่างกันสามสถานการณ์ที่กำหนดไว้อย่างดีเสมอ รู้นี้จะช่วยให้คุณรู้ได้ทันทีค่อนข้างมากเกี่ยวกับaและbเพียงแค่มองa::b, a.bหรือa->bตามลำดับในรหัสที่คุณมองไปที่

  1. a::bถูกนำมาใช้เฉพาะในกรณีที่bเป็นสมาชิกของชั้น (หรือ anamespace) นั่นคือในกรณีนี้aจะเป็นชื่อคลาสเสมอ (หรือเนมสเปซ)

  2. a.bถูกนำมาใช้เฉพาะในกรณีที่bเป็นสมาชิกของวัตถุ a(หรือการอ้างอิงไปยังวัตถุ) ดังนั้นสำหรับa.b, aมักจะเป็นวัตถุที่เกิดขึ้นจริง (หรือการอ้างอิงไปยังวัตถุ) ของชั้น

  3. a->b(*a).bคือเดิมสัญกรณ์ชวเลข อย่างไรก็ตาม->เป็นเพียงโอเปอเรเตอร์การเข้าถึงสมาชิกที่สามารถโอเวอร์โหลดได้ดังนั้นหากaเป็นวัตถุของคลาสที่โอเวอร์โหลดoperator->(ชนิดทั่วไปคือพอยน์เตอร์พอยน์เตอร์และตัววนซ้ำ) ความหมายคือสิ่งที่ผู้ออกแบบคลาสดำเนินการ เพื่อสรุป: ด้วยa->bถ้าaเป็นตัวชี้bจะเป็นสมาชิกของวัตถุที่ตัวชี้aอ้างถึง อย่างไรก็ตามหากaเป็นวัตถุของคลาสที่โอเวอร์โหลดโอเปอเรเตอร์นี้แล้วฟังก์ชันโอเปอเรเตอร์ที่โอเวอร์โหลดoperator->()จะถูกเรียกใช้


พิมพ์เล็ก:

  • ใน C ++, ประเภทประกาศเป็นclass, structหรือunionจะถือว่าเป็น "ประเภทคลาส" ดังนั้นข้างต้นหมายถึงทั้งสามของพวกเขา
  • การอ้างอิงคือนามแฝงของวัตถุดังนั้นฉันควรเพิ่ม "หรือการอ้างอิงถึงตัวชี้" ใน # 3 ด้วย อย่างไรก็ตามฉันคิดว่าสิ่งนี้จะสร้างความสับสนมากกว่าที่เป็นประโยชน์เนื่องจากการอ้างอิงถึงพอยน์เตอร์ ( T*&) นั้นไม่ค่อยมีใครเคยใช้
  • ตัวดำเนินการจุดและลูกศรสามารถใช้เพื่ออ้างถึงสมาชิกคลาสคงที่จากวัตถุแม้ว่าพวกเขาจะไม่ได้เป็นสมาชิกของวัตถุ (ขอบคุณ Oli ที่ชี้เรื่องนี้ออกมา!)

10
มันควรจะมีการชี้แจงว่า.และ->อาจถูกใช้เพื่อเข้าถึง statics ระดับผ่านวัตถุแม้ว่าพวกเขาจะไม่ "สมาชิกของวัตถุ" อย่างเคร่งครัด
Oliver Charlesworth

@Oli: นั่นเป็นเรื่องจริง ฉันเพิ่มลงในสิ่งพิมพ์ขนาดเล็กเนื่องจากฉันคิดว่ามันไม่ธรรมดาและสำคัญพอที่จะแสดงไว้ในข้อความหลัก
sbi

3
เพื่อความสมบูรณ์มันอาจคุ้มค่าที่จะชี้ให้เห็นว่าoperator*()สามารถรับภาระมากเกินไปและไม่มีสิ่งใดบังคับให้เกินพิกัดให้สอดคล้องกับoperator->()! (ฉันไม่ได้ลงคะแนน BTW เพิ่งมาถึงที่นี่ผ่านลำดับที่ซ้ำซ้อนกันนาน)
juanchopanza

@OliCharlesworth คุณจะรู้ว่าที่ระบุไว้ในมาตรฐาน C ++?
สุกร

1
@juanchopanza: คุณไม่สามารถรับพฤติกรรมการผูกมัด->โดยการใช้งานมากเกินไปoperator*และใช้.งานได้ operator->เกินพิกัดเท่านั้นที่จะได้รับ
Ben Voigt

36

แนะนำทางเลือกสำหรับ sbi's point 3

a->bจะใช้ถ้าaเป็นตัวชี้เท่านั้น มันเป็นชวเลข(*a).bที่bสมาชิกของวัตถุที่aจุดที่จะต้อง C ++ มีพอยน์เตอร์สองประเภทคือ "พอยน์เตอร์" ปกติและสมาร์ทพอยน์เตอร์ สำหรับคำแนะนำปกติเช่นการดำเนินการคอมไพเลอร์A* a ->สำหรับคำแนะนำสมาร์ทเช่นstd::shared_ptr<A> a, เป็นฟังก์ชั่นสมาชิกของชั้นเรียน->shared_ptr

เหตุผล: กลุ่มเป้าหมายของคำถามที่พบบ่อยนี้ไม่ได้เขียนตัวชี้สมาร์ท พวกเขาไม่จำเป็นต้องรู้ว่า->มีชื่อเรียกจริง ๆoperator->()หรือว่าเป็นวิธีการเข้าถึงสมาชิกเท่านั้นที่สามารถโหลดมากเกินไป


4
ไม่ว่าฉันจะเห็นด้วยหรือไม่+1ก็ตาม
sbi

2
การมีความยุติธรรม->นั้นมากเกินไปสำหรับตัววนซ้ำมาตรฐานซึ่งโปรแกรมเมอร์ C ++ คนใดควรจะพบกันเร็ว ๆ นี้ดังนั้นการบอกว่ามันถูกใช้เฉพาะสำหรับตัวชี้อาจทำให้เกิดความสับสน
Kiscsirke

@Kiscsirke "โปรแกรมเมอร์ C ++ ธรรมดา" ไม่จำเป็นต้องเขียนตัวชี้สมาร์ทหรือตัววนซ้ำเพียงแค่ใช้มัน "การอ้างอิงเช่นตัวชี้" ใช้กับทั้งคู่
Caleth

0
#include <iostream>
#include <string>

using namespace std;

class Human {
private:
    int age;

public:
    string name;

    Human(int humanAge, string humanName) 
         : age(humanAge), name(std::move(humanName)) {}

    void DoSomething() {
        cout << age << endl;
    }

    static void DisplayAge(const Human& person) {
        cout << person.age << endl;
    }

    // ...
};

int main() {
    // Usage of Dot(.) 
    Human firstMan(13, "Jim"); // firstMan is an instance of class Human
    cout << firstMan.name << endl; // accessing member attributes
    firstMan.DoSomething(); // accessing member functions

    // Usage of Pointer Operator (->)
    Human* secondMan = new Human(24, "Tom");
    cout << secondMan->name << endl; // accessing member attributes
    secondMan->DoSomething(); // accessing member functions
    cout << (*secondMan).name << endl; // accessing member attributes
    (*secondMan).DoSomething(); // accessing member functions

    // Usage of Double Colon (::)
    Human::DisplayAge(firstMan);
    firstMan.DisplayAge(firstMan); // ok but not recommended
    secondMan->DisplayAge(firstMan); // ok but not recommended

    delete(secondMan);

    return 0;
}

จากตัวอย่างโค้ดข้างต้นเราจะเห็นว่า:
* การเข้าถึงสมาชิก (คุณสมบัติและฟังก์ชั่น) จากอินสแตนซ์ (หรือวัตถุ) โดยใช้ตัวดำเนินการ dot ( .)
* การเข้าถึงสมาชิก (คุณลักษณะและฟังก์ชัน) จากตัวชี้ไปยังวัตถุ (หรือสร้างขึ้นโดยnew) การใช้ตัวดำเนินการตัวชี้ ( ->)
* การเข้าถึงฟังก์ชั่นสมาชิกแบบคงที่จากคลาสตัวเองโดยไม่ต้องมีวัตถุเป็นที่จับโดยใช้ลำไส้ใหญ่คู่ ( ::) [ หมายเหตุ:คุณสามารถเรียกใช้ฟังก์ชันสมาชิกแบบคงที่จากอินสแตนซ์ที่มี.หรือ->ไม่แนะนำให้ใช้]


@ sbi ไม่พอใจเลยฮ่า ๆ ฉันรู้ว่ามันเป็นการทำซ้ำบางอย่าง ฉันแค่ต้องการยกตัวอย่างชัดเจนเพื่อแสดงวิธีใช้ และที่ฉันบอกว่า->สามารถใช้งานได้โดยตัวชี้ที่จัดสรรบนฮีปด้วยnew? ด้านล่างไอเท็มที่สองฉันคิดว่าฉันชัดเจนว่า->สำหรับตัวชี้ และก่อนที่คุณจะลงคะแนนคุณควรลองclassName::non_static_member_function()ด้วย c ++ 14 ด้วยตัวเอง การอ้างอิงไม่ใช่ตัวชี้เพื่อให้สามารถใช้.และฉันจะทำให้ชัดเจนขึ้นในคำตอบของฉัน
Hu Xixi

0

ตัวดำเนินการ Dot ถูกใช้ในสถานการณ์การเลือกสมาชิกโดยตรง

print(a.b)

ที่นี่เรามีการเข้าถึงซึ่งเป็นสมาชิกโดยตรงของวัตถุb aดังนั้นส่วนใหญ่aเป็นวัตถุและbเป็นสมาชิก (ฟังก์ชั่น / ตัวแปร ฯลฯ ) aของ


ตัวดำเนินการลูกศรใช้ในสถานการณ์การเลือกสมาชิกทางอ้อม

print(a->b)

ที่นี่เรามีการเข้าถึงซึ่งเป็นสมาชิกของวัตถุที่เป็นชี้ไปตามb aมันเป็นชวเลข(*a).bและที่นี่aเป็นตัวชี้ไปยังวัตถุและbเป็นสมาชิกของวัตถุนั้นเป็นหลัก


ตัวดำเนินการ Double Colon (Scope) ใช้ในสถานการณ์การเลือกสมาชิกโดยตรงที่เกี่ยวข้องกับเนมสเปซ

print(a::b)

ที่นี่เรามีการเข้าถึงbซึ่งเป็นสมาชิกของชนชั้น / namespace aดังนั้น, ส่วนใหญ่aเป็นชั้น / namespace และbเป็นสมาชิก (ฟังก์ชั่น / ตัวแปร ฯลฯ ) aของ

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