C ++ ให้เพื่อนหรือไม่ให้เพื่อน


19

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

ฉันไม่ชอบพวกเขาโดยสัญชาตญาณเพราะความสามารถในการหลีกเลี่ยงความปลอดภัยที่ Encapsulation และ Data ซ่อนไว้ฉันอ่านบทความสองสามเรื่องบนอินเทอร์เน็ตและบางคนคิดว่ามันเป็นความคิดที่ดีที่มีการใช้งานที่ถูกกฎหมาย

ผู้เชี่ยวชาญ OOP จะพูดอย่างไรเกี่ยวกับเพื่อน fucntions ใน C ++ ฉันควรอ่านอย่างคร่าวๆหรือควรเรียนรู้เพิ่มเติมเกี่ยวกับเรื่องนี้หรือไม่


@ ทั้งหมด: คำตอบและความคิดเห็นที่ยอดเยี่ยมนี่เป็นวิธีที่ดีในการเรียนรู้ไม่มีทางที่ฉันจะได้เรียนรู้เกี่ยวกับเพื่อน ๆ ในรายละเอียดในตำราเรียน
nikhil

คำตอบ:


13

ไม่สะดวกเสมอที่จะทำให้ฟังก์ชั่นทั้งหมดที่เกี่ยวข้องกับสมาชิกคลาส C ++ ของคลาสนั้น ตัวอย่างเช่นลองนึกภาพการใช้พีชคณิตเวกเตอร์ด้วยการคูณสเกลาร์ เราต้องการเขียน:

 double a;
 Vector v, w;
 w = v * a;

เราสามารถทำได้ด้วยฟังก์ชั่นสมาชิก:

public class Vector {
 ...
 Vector operator*(double a);
}

แต่เราต้องการเขียน:

w = a * v

สิ่งนี้ต้องการฟังก์ชั่นฟรี:

 Vector operator*(double a, Vector v)

friendคำหลักที่ถูกบันทึกอยู่ใน c ++ เพื่อสนับสนุนการใช้งานนี้ ฟังก์ชั่นฟรีเป็นส่วนหนึ่งของการนำคลาสเวกเตอร์ไปใช้และควรประกาศในส่วนหัวเดียวกันและนำไปใช้ในไฟล์ต้นฉบับเดียวกัน

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


3
"นี่ต้องใช้ฟังก์ชันฟรี" inline Vector operator*(double a, Vector v) { return v*a; }ไม่มีก็ไม่ได้: วิธีการแก้ปัญหาที่ยอมรับในความเป็นจริง
MSalters

1
@MSalters: จุดดี ฉันเลือกตัวอย่างที่น่าสงสาร ฉันคิดว่าฟังก์ชั่นอินไลน์ของคุณเป็นฟังก์ชั่นฟรีตามคำนิยาม แต่ไม่จำเป็นต้องมีการประกาศเพื่อน
วินไคลน์

4
@MSalters: ถูกต้องก็ต่อเมื่อ * เป็นคอมมิชชันที่เคารพต่อ a และ v (x) หากส่วนประกอบของเวกเตอร์เป็นแบบทั่วไป (ไม่จำเป็นต้องมีสเกลาร์) คุณจะต้องรักษาลำดับตัวถูกดำเนินการ
Emilio Garavaglia

มันค่อนข้างเชิงทฤษฎี บางทีอาจเป็นกรณีธรรมดาที่ไม่ใช่การสับเปลี่ยน inline Vector operator*(double a, Vector v) { return -v*a; }และอาจยังไม่ต้องการมิตรภาพ
MSalters

16

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

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


+1 สำหรับฟังก์ชั่นเพื่อนไม่แตกต่างจากฟังก์ชั่นสมาชิกในแง่ของการห่อหุ้ม สิ่งนี้เป็นจริงสำหรับฟังก์ชั่นสมาชิกสาธารณะเท่านั้น
TheFogger

1
@TheFogger: คุณสามารถfriendใช้ฟังก์ชั่นที่เป็น "ส่วนตัว" ได้เช่นประกาศใน TU เดียวเท่านั้น
DeadMG

5

หากคุณหลงใหลในสิ่งที่คุณทำคุณจะได้เรียนรู้ทุกอย่างเกี่ยวกับ C ++ เรียนรู้สิ่งที่พวกเขาใช้สำหรับวิธีการใช้พวกเขาแล้ว - และจากนั้น - ตัดสินใจว่าจะไม่ใช้พวกเขา อย่างน้อยที่สุดคุณจะต้องเตรียมพร้อมเมื่ออ่านรหัสของผู้อื่นที่ใช้แง่มุมของ C ++


5

" ผู้เชี่ยวชาญ OOP จะพูดอะไร ... " ส่วนใหญ่ขึ้นอยู่กับว่าเขาเป็นผู้เชี่ยวชาญในภาษา C ++ อย่างไร - โดยข้อกำหนดของตัวเอง - ไม่ใช่ (และไม่ต้องการเป็น) ภาษาสำหรับคนเจ้าระเบียบ

OOP Zealots ไม่ได้ใช้ C ++ (พวกเขาชอบ Smalltalk และชอบ Java)

การทำงานของโปรแกรม Zelots ไม่ได้ใช้ C ++ (พวกเขาชอบ LISP และผู้สืบทอด)

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

และจากจุดยืน "การทำงาน" ระหว่างa.fn(b)และfn(a,b)ไม่มีความแตกต่าง (ที่fnเป็นเพื่อน): บุคคลที่เกี่ยวข้องจะเหมือนกัน เพียงแค่หนึ่งไวยากรณ์อาจจะเหมาะสมกว่าอีกถ้า Fn เป็นสับเปลี่ยนเกี่ยวกับaและb, fn(a,b)อาจจะเป็นที่เหมาะสมมากขึ้นแล้วa.fn(b)(. ที่รูปลักษณ์ที่มี "บทบาทพิเศษ" ว่าในความเป็นจริงมันไม่ได้)


1
“ OOP zealots” ที่ชอบ Java ไม่เข้าใจ OOP Getters? Setters? ไม่มีไวยากรณ์ง่าย ๆ สำหรับการปิด? เพื่อถอดความอลันเคย์นี่ไม่ใช่วิธีที่เขาจินตนาการ OOP
Konrad Rudolph

@ Konrad: zealots เป็นเซตที่ไม่ จำกัด มี zealot เสมอ zealot มากกว่า zealot ที่ให้ไว้
Emilio Garavaglia

ฉันต้องบอกว่าฉัน upvoted เพราะฉันชอบย่อหน้าสุดท้าย ทำให้รู้สึกมาก
julealgon

5

"เพื่อน" ละเมิดการห่อหุ้มหรือไม่?

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


2

c ++ คำถามที่พบบ่อยคือรวบรัด:

ใช้สมาชิกเมื่อคุณสามารถและเพื่อนเมื่อคุณต้อง

คำถามที่พบบ่อยนำเสนอหนึ่งในวิธีที่มีประโยชน์มากกว่าในการคิดเกี่ยวกับมิตรภาพ:

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

บางทีการใช้ฟังก์ชั่นเพื่อนที่พบบ่อยที่สุดคือการโหลดมากเกินไป << สำหรับ I / O


0

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

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


-2

ฟังก์ชั่นเพื่อน C ++ เกี่ยวข้องอย่างใกล้ชิดกับฟังก์ชั่นต่อไปนี้:

  1. ฟังก์ชั่นฟรี
  2. ฟังก์ชั่นคงที่
  3. ฟังก์ชั่นเพื่อน

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

class B;
class A {
public:
    friend void f(A &a, B &b);
private:
    int m_a;
};
class B {
public:
   friend void f(A &a, B &b);
private:
   int m_b;
};
void f(A &a, B &b) { /* uses both A's and B's private data */ }

ข้อแตกต่างระหว่างฟังก์ชั่นคงที่และฟังก์ชั่นเพื่อนคือฟังก์ชั่นเพื่อนสามารถใช้หลายชั้นเรียน

การใช้กลไกเพื่อนใน c ++ ต้องการโปรแกรมเมอร์ที่มีประสบการณ์ประมาณ 10-15 ปีกับวิธีการเขียนโปรแกรม c ++ ดังนั้นในขั้นต้นคุณควรหลีกเลี่ยง มันเป็นคุณสมบัติขั้นสูง


7
และคุณได้รับ 10-15 ปีเป็นอย่างไร?
DeadMG

10-15 ปีมาจากเวลาที่มันจำเป็นจริงๆ
tp1

3
ดังนั้นคุณจึงสร้างตัวเลขโดยพลการ
DeadMG

3
-1: "คุณควรหลีกเลี่ยง" คุณลักษณะทั้งหมดของ C ++ ถูกสร้างขึ้นเพื่อแก้ปัญหา เมื่อเกิดปัญหาขึ้นให้ใช้คุณสมบัติที่เหมาะสม
วินไคลน์

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