ตัวดำเนินการเข้าถึงสมาชิกมากเกินไป ->,. *


130

ผมเข้าใจมากไปผู้ประกอบการส่วนใหญ่มีข้อยกเว้นของผู้ประกอบการเข้าถึงสมาชิกที่->, .*, ->*ฯลฯ

โดยเฉพาะอย่างยิ่งสิ่งที่ส่งผ่านไปยังฟังก์ชันตัวดำเนินการเหล่านี้และสิ่งที่ควรส่งคืน?

ฟังก์ชันตัวดำเนินการ (เช่นoperator->(...)) รู้ได้อย่างไรว่าสมาชิกใดถูกอ้างถึง? มันรู้ได้ไหม? มันจำเป็นต้องรู้หรือไม่?

สุดท้ายมีข้อพิจารณาใดบ้างที่ต้องนำมาพิจารณา? ตัวอย่างเช่นเมื่อมีการใช้งานมากเกินไปoperator[]โดยทั่วไปคุณจะต้องมีทั้งเวอร์ชัน const และไม่ใช่ const ตัวดำเนินการเข้าถึงสมาชิกต้องการเวอร์ชัน const และ non-const หรือไม่?


1
ฉันเชื่อว่า C ++ ข้างต้น - คำถามที่พบบ่อยเกี่ยวกับ Q ทั้งหมดที่ถามใน Q ข้างต้น
Alok Save

constและไม่ใช่constเวอร์ชันoperator->ที่ไม่จำเป็นแต่การให้ทั้งสองอย่างจะมีประโยชน์
Fred Foo

1
ดูสิ่งนี้ด้วย: yosefk.com/c++fqa/operator.html
György Andrasek

9
@Als: คำถามที่พบบ่อยไม่ได้อธิบายวิธีการโอเวอร์โหลด->*และ.*. ในความเป็นจริงมันไม่ได้พูดถึงพวกเขา! ฉันรู้สึกว่าเป็นเรื่องยากที่จะอยู่ในคำถามที่พบบ่อย แต่ฉันยินดีที่จะเชื่อมโยงคำถามนี้จากคำถามที่พบบ่อย โปรดอย่าปิดสิ่งนี้เนื่องจากเป็นการหลอกลวงคำถามที่พบบ่อย!
sbi

@sbi ฉันล้มเหลวในการค้นหาลิงก์ไปยังคำถามนี้จากคำถามที่พบบ่อย (น่ากลัว) ของคุณและลงเอยด้วยการถามคำถามซ้ำ คุณช่วยให้ชัดเจนขึ้นได้ไหม (ขออภัยหากเห็นได้ชัดแล้ว)
P i

คำตอบ:


145

->

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

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

struct client
    { int a; };

struct proxy {
    client *target;
    client *operator->() const
        { return target; }
};

struct proxy2 {
    proxy *target;
    proxy &operator->() const
        { return * target; }
};

void f() {
    client x = { 3 };
    proxy y = { & x };
    proxy2 z = { & y };

    std::cout << x.a << y->a << z->a; // print "333"
}

->*

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

ในคำอื่น ๆ หนึ่งนี้เป็นเพียงผู้ประกอบการไบนารีปกติเช่น+, และ- /ดูเพิ่มเติม: ตัวดำเนินการฟรี -> * มีความชั่วร้ายมากเกินไปหรือไม่?

.* และ .

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

การบรรทุกเกินพิกัด->, ->*, .และ.*สามารถเติมเต็มในกรณีที่มีการแสดงออกจะไม่ได้กำหนดก็ไม่สามารถเปลี่ยนความหมายของการแสดงออกที่จะสามารถใช้งานได้โดยไม่มีการบรรทุกเกินพิกัด


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

6
@ แมตต์ดีnewมักจะโอเวอร์โหลดหรือกฎการโอเวอร์โหลดไม่ได้ใช้กับมันจริงๆ (13.5 / 5: ฟังก์ชั่นการจัดสรรและการจัดสรรตำแหน่งตัวดำเนินการใหม่ตัวดำเนินการใหม่ [] การลบตัวดำเนินการและการลบตัวดำเนินการ [] อธิบายไว้อย่างสมบูรณ์ใน 3.7 0.4. คุณลักษณะและข้อ จำกัด ที่พบในส่วนที่เหลือของขนี้ไม่สามารถใช้กับพวกเขาเว้นแต่จะระบุไว้อย่างชัดเจนใน 3.7.4.) แต่การบรรทุกเกินพิกัดเอก&หรือไบนารี&&, ||หรือ,หรือเพิ่มทับถมของoperator=หรือการบรรทุกเกินพิกัดเพียงเกี่ยวกับอะไรสำหรับ unscoped ประเภทการแจงนับสามารถเปลี่ยนความหมายของนิพจน์ แจงแถลงขอบคุณ!
Potatoswatter

41

Operator -> เป็นพิเศษ

"มันมีข้อ จำกัด เพิ่มเติมที่ผิดปกติ: ต้องส่งคืนวัตถุ (หรือการอ้างอิงไปยังวัตถุ) ที่มีตัวดำเนินการแยกตัวชี้ด้วยหรือจะต้องส่งคืนตัวชี้ที่สามารถใช้เพื่อเลือกสิ่งที่ลูกศรตัวดำเนินการ dereference ตัวชี้ชี้อยู่ " Bruce Eckel: การคิด CPP Vol-one: operator->

ฟังก์ชันพิเศษมีไว้เพื่อความสะดวกคุณจึงไม่ต้องโทร

a->->func();

คุณสามารถทำได้ง่ายๆ:

a->func();

นั่นทำให้ตัวดำเนินการ -> แตกต่างจากตัวดำเนินการอื่นที่โอเวอร์โหลด


3
คำตอบนี้สมควรได้รับเครดิตมากขึ้นคุณสามารถดาวน์โหลดหนังสือของ Eckel ได้จากลิงค์นั้นและข้อมูลอยู่ในบทที่ 12 ของเล่มที่หนึ่ง
P i

26

คุณไม่สามารถเข้าถึงสมาชิกมากเกินไป.(เช่นส่วนที่สองของสิ่งที่->ทำ) อย่างไรก็ตามคุณสามารถโอเวอร์โหลดโอเปอเรเตอร์unary dereferencing* (เช่นส่วนแรกของสิ่งที่->ทำ)

ตัวดำเนินการ C ++ ->นั้นโดยพื้นฐานแล้วการรวมกันของสองขั้นตอนและเป็นที่ชัดเจนหากคุณคิดว่าx->yเทียบเท่ากับ(*x).y. C ++ ช่วยให้คุณปรับแต่งสิ่งที่จะทำกับ(*x)ส่วนเมื่อxเป็นอินสแตนซ์ของชั้นเรียนของคุณได้

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


2
คำอธิบายที่ยอดเยี่ยม! ฉันเดาว่านั่นหมายความว่าเหมือนกันเพราะ->*เทียบเท่ากับรูปแบบของ(*x).*?
Bingo

10

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

นอกจากนี้ฉันไม่เห็นเหตุผลว่าทำไมคุณไม่สามารถระบุเวอร์ชัน const และ non-const ได้


7

เมื่อคุณโอเวอร์โหลดตัวดำเนินการ -> () (ไม่มีการส่งผ่านอาร์กิวเมนต์ที่นี่) สิ่งที่คอมไพลเลอร์ทำคือการเรียก -> วนซ้ำจนกว่าจะส่งกลับตัวชี้จริงไปยังประเภท จากนั้นใช้สมาชิก / วิธีการที่ถูกต้อง

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

สำหรับ constness - ได้รับคำตอบในความคิดเห็นและคำตอบอื่น ๆ (คุณสามารถและควรระบุทั้งสองอย่าง)

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