เหตุใดออบเจ็กต์ของคลาสเดียวกันจึงเข้าถึงข้อมูลส่วนตัวของกันและกันได้


99

เหตุใดออบเจ็กต์ของคลาสเดียวกันจึงเข้าถึงข้อมูลส่วนตัวของกันและกันได้

class TrivialClass {
public: 
  TrivialClass(const std::string& data) :
    mData(data) {};

  const std::string& getData(const TrivialClass& rhs) const {
    return rhs.mData;
  };

private:
  std::string mData;
};

int main() {
  TrivialClass a("fish");
  TrivialClass b("heads");

  std::cout << "b via a = " << a.getData(b) << std::endl;
  return 0;
}

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


18
ในฐานะที่เป็นจุดเริ่มต้นคุณจะไม่สามารถใช้ตัวสร้างการคัดลอกใด ๆ ได้อย่างถูกต้องนอกจากคลาสที่ง่ายที่สุด คุณสามารถคิดว่าชั้นเรียนเป็นเพื่อนที่ดีที่สุดของพวกเขาเอง:-)
คาเมรอน

4
คิดถึงความเป็นส่วนตัวจากลูกค้าของตน แต่พนักงานทุกคนในชั้นเรียนสามารถเข้าถึงได้
Martin Beckett

ขอบคุณคาเมรอน นั่นเป็นเหตุผล แต่เหตุใดจึงไม่ จำกัด การเข้าถึงนี้ให้คัดลอกตัวสร้างและตัวดำเนินการกำหนดเท่านั้น
Keith

5
วัตถุประเภทเดียวกันมักจะโต้ตอบกันมาก และไม่มีใครบังคับให้คุณเขียนวิธีการที่ให้ข้อมูลส่วนตัวของอินสแตนซ์อื่น :)
UncleBens

4
เนื่องจากในเวลาคอมไพล์คอมไพเลอร์ไม่มีวิธีระบุอ็อบเจ็กต์เดียวกัน การบังคับใช้การเข้าถึงดังกล่าวจะต้องมีการสนับสนุนรันไทม์
Chethan

คำตอบ:


80

เพราะนั่นคือวิธีการทำงานใน C ++ ในการควบคุมการเข้าถึง C ++ ทำงานบนพื้นฐานต่อคลาสไม่ใช่แบบต่ออ็อบเจ็กต์

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

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


9
+1. C ++ มีขนาดใหญ่ในกลไกการคอมไพล์ไทม์ไม่ใช่เรื่องใหญ่ในกลไกรันไทม์ กฎทั่วไปค่อนข้างดี
Nemo

4
"เป็นไปไม่ได้เลยที่จะใช้การควบคุมการเข้าถึงต่อวัตถุที่มีความหมายในเวลาคอมไพล์" ทำไมจะไม่ล่ะ? ในvoid X::f(X&x)คอมไพเลอร์สามารถแยกแยะthis->aและx.a. เป็นไปไม่ได้ (เสมอไป) ที่คอมไพลเลอร์จะรู้ว่า*thisและxเป็นอ็อบเจ็กต์เดียวกันหากx.f(x)ถูกเรียกใช้ แต่ฉันเห็นได้ดีว่านักออกแบบภาษาพบว่าสิ่งนี้ใช้ได้
André Caron

@ AndréCaronฉันคิดว่านี่เป็นกาต้มน้ำปลาที่ใหญ่กว่ามากแล้วคุณก็ทำมันออกมา เมื่อ inlining ไม่ได้เกิดขึ้นคอมไพเลอร์จะต้องทำการตรวจสอบว่าthisและ&xจะเหมือนกัน ที่จะทำให้มันเลวร้ายนี้จริงสิ้นสุดขึ้นเป็นปัญหาแม้จะมีX::f(Y& y)เพราะวัตถุที่เป็นรูปธรรมของเราอาจจะเป็นประเภทZที่สืบทอดจากทั้งสองและX Yในระยะสั้นมันเป็นเรื่องยุ่งจริงๆไม่ใช่นักแสดงยากที่จะทำงานอย่างสมเหตุสมผลกับ MI
Nir Friedman

@NirFriedman ฉันคิดว่าคุณเข้าใจผิดคำแนะนำ เมื่อทำการคอมไพล์X::f(X& x)หากมีการเข้าถึงx.aมันจะไม่คอมไพล์ ไม่มีการเปลี่ยนแปลงอื่นใดไม่จำเป็นต้องใส่การตรวจสอบดังนั้นประสิทธิภาพของโปรแกรมที่ยังใช้ได้จะไม่ได้รับผลกระทบ และไม่ได้แนะนำว่าเป็นการเปลี่ยนแปลง C ++ ที่มีอยู่อย่างสิ้นเชิง แต่เป็นสิ่งที่นักออกแบบสามารถทำได้เมื่อแนะนำprivateครั้งแรก
Alexey Romanov

31

"ส่วนตัว" ไม่ใช่กลไกการควบคุมการเข้าถึงในแง่ของ "ฉันทำให้รูปภาพของฉันเป็นแบบส่วนตัวบน Facebook ดังนั้นคุณจึงไม่สามารถเห็นได้"

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

หากคุณต้องการการควบคุมการเข้าถึงที่แท้จริงคุณควรใช้เทคนิคการรักษาความปลอดภัยข้อมูลของแท้


13

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

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

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

นอกจากนี้การควบคุมการเข้าถึงส่วนใหญ่มาจากมุมมองการเขียนโปรแกรม / ภาษาสำหรับวิธีการแยกส่วน / ควบคุมการเข้าถึงรหัส / สมาชิกไม่ใช่ข้อมูล


12

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

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

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


4

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


1

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


-8

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

แนวคิดนี้ใช้ได้กับสถานการณ์อื่น ๆ เช่น:

class cMyClass
{
public:
   // ...
   // omitted for clarity
   // ...

   void Withdraw(int iAmount)
   {
      iTheSecretVault -= iAmount;
   }

private:
   int iTheSecretVault;
};

ใครจะถอนเงินได้อย่างไร? :)


3
ตัวอย่างนี้ไม่เกี่ยวข้องกับอินสแตนซ์คลาสหนึ่งที่เข้าถึงสมาชิกข้อมูลส่วนตัวของอินสแตนซ์อื่น
André Caron

@ อันเดร "แนวคิดนี้ใช้ได้กับสถานการณ์อื่นด้วยเช่น ... "
YeenFei

^ "สถานการณ์อื่น ๆ " ไม่อยู่ในหัวข้อตามคำจำกัดความดังนั้นตัวอย่างของคุณจึงไม่มีความเกี่ยวข้อง (และฉันไม่แน่ใจว่าจะให้ข้อมูลที่อื่นด้วย)
underscore_d

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