เหตุใดฉันจึงเข้าถึงตัวแปรส่วนตัวในตัวสร้างการคัดลอกได้


88

ฉันได้เรียนรู้ว่าฉันไม่สามารถเข้าถึงตัวแปรส่วนตัวได้โดยใช้ get-function ในคลาสเท่านั้น แต่ทำไมฉันถึงเข้าถึงได้ในตัวสร้างสำเนา

ตัวอย่าง:

Field::Field(const Field& f)
{
  pFirst = new T[f.capacity()];

  pLast = pFirst + (f.pLast - f.pFirst);
  pEnd  = pFirst + (f.pEnd - f.pFirst);
  std::copy(f.pFirst, f.pLast, pFirst);
}

คำประกาศของฉัน:

private:
  T *pFirst,*pLast,*pEnd;

เนื่องจากตัวสร้างการคัดลอกเป็นสมาชิกคลาสโดยค่าเริ่มต้นและอื่น ๆ ก็เช่นกัน
DumbCoder

+ 53 / -0? ใครโหวตเรื่องนี้ จะลอกยังไงอีกล่ะ!? (มาหักล้างตัวเลือกที่ไม่ใช่ทางเลือก: สร้างข้อมูลอ้างอิงสาธารณะสำหรับสมาชิกส่วนตัวแต่ละคนหรือไม่ถ้าอย่างนั้นพวกเขาก็ไม่เป็นส่วนตัวเลยสร้างสาธารณะconst&หรือตามมูลค่าสำหรับแต่ละคน? จากนั้นพวกเขาจะ 'เขียนส่วนตัว' เท่านั้น & สำหรับค่าทรัพยากรที่สิ้นเปลืองและล้มเหลวสำหรับสมาชิกที่ไม่สามารถคัดลอกได้) ฉันรู้สึกงุนงงกับความสำเร็จของคำถามที่ว่างเปล่าเช่นนี้โดยถามเกี่ยวกับการสร้างสำเนาโดยไม่สนใจว่ามันหมายถึงอะไรและไม่มีคำตอบใดที่ใช้ตรรกะพื้นฐานในการหักล้างมัน พวกเขาอธิบายถึงเทคนิคแบบแห้ง แต่มีคำตอบที่ง่ายกว่ามากสำหรับคำถามนี้
underscore_d

9
@underscore_d "คุณจะคัดลอกอย่างไร" เป็นคำตอบที่แปลกมากในความคิดของฉัน ก็เหมือนกับการตอบว่า "แรงโน้มถ่วงทำงานอย่างไร" กับ "อะไรจะพังอีก!" การห่อหุ้มระดับคลาสที่สับสนกับการห่อหุ้มระดับออบเจ็กต์นั้นพบได้บ่อย เป็นเรื่องตลกที่คุณดูเหมือนจะคิดว่าเป็นคำถามโง่ ๆ และคำตอบควรชัดเจน โปรดทราบว่าการห่อหุ้มใน Smalltalk (เนื้อหาตามแบบฉบับของภาษา OO) เกิดขึ้นได้จริงในระดับวัตถุ
aioobe

@aioobe จุดดีขอบคุณ ความคิดเห็นของฉันค่อนข้างรุนแรง - บางทีเครื่องชงกาแฟเสียในวันนั้น ฉันขอขอบคุณที่คุณชี้ให้เห็นว่าทำไมคำถามนี้ถึงได้รับความนิยมโดยเฉพาะอย่างยิ่งในบรรดาผู้ที่มาจากภาษา OO อื่น ๆ (และอาจมากกว่านั้น) ในความเป็นจริงเป็นที่ถกเถียงกันอยู่ว่าความคิดเห็นของฉันเป็นสิ่งที่ "พูดไม่ชัด" เนื่องจากฉันเขียนจากมุมมองของคนที่ส่วนใหญ่เขียนโปรแกรมใน C ++ นอกจากนี้ยังรักการเปรียบเทียบแรงโน้มถ่วง!
underscore_d

คำตอบ:


33

IMHO คำตอบที่มีอยู่ทำงานได้ไม่ดีในการอธิบาย "ทำไม" ของสิ่งนี้ - เน้นย้ำมากเกินไปว่าพฤติกรรมใดถูกต้อง "ตัวปรับแต่งการเข้าถึงทำงานในระดับชั้นเรียนไม่ใช่ในระดับวัตถุ" - ใช้ แต่ทำไม?

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

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

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

โดยเฉพาะอย่างยิ่งการดำเนินการเหล่านี้อาจต้องการใช้ประโยชน์จากการเข้าถึงแบบส่วนตัวเพื่อทำสิ่งต่างๆเช่น:

  • (ตัวสร้างการคัดลอก) ใช้สมาชิกส่วนตัวของอ็อบเจ็กต์ "rhs" (ด้านขวามือ) ในรายการตัวเริ่มต้นเพื่อให้ตัวแปรสมาชิกถูกสร้างขึ้นเองแทนที่จะเป็นค่าเริ่มต้นที่สร้างขึ้น (แม้ว่าจะถูกกฎหมาย) จากนั้นก็กำหนดด้วย (อีกครั้ง, ถ้าถูกกฎหมาย)
  • แบ่งปันทรัพยากร - จัดการไฟล์ส่วนหน่วยความจำที่ใช้ร่วมกันshared_ptrเพื่ออ้างอิงข้อมูลเป็นต้น
  • เป็นเจ้าของสิ่งต่างๆเช่นauto_ptr<>"ย้าย" ความเป็นเจ้าของไปยังวัตถุที่กำลังก่อสร้าง
  • คัดลอก "แคช" ส่วนตัวการปรับเทียบหรือสมาชิกของรัฐที่จำเป็นในการสร้างวัตถุใหม่ในสถานะที่สามารถใช้งานได้อย่างเหมาะสมโดยไม่ต้องสร้างใหม่ตั้งแต่ต้น
  • คัดลอก / เข้าถึงข้อมูลการวินิจฉัย / ติดตามที่เก็บไว้ในอ็อบเจ็กต์ที่ถูกคัดลอกซึ่งไม่สามารถเข้าถึงได้ผ่าน API สาธารณะ แต่อาจถูกใช้โดยอ็อบเจ็กต์ข้อยกเว้นหรือการบันทึกในภายหลัง (เช่นบางอย่างเกี่ยวกับเวลา / สถานการณ์เมื่ออินสแตนซ์ที่สร้างขึ้น "ดั้งเดิม" ที่ไม่ใช่การคัดลอก ถูกสร้างขึ้น)
  • ดำเนินการคัดลอกข้อมูลบางอย่างที่มีประสิทธิภาพมากขึ้นเช่นวัตถุอาจมีเช่นunordered_mapสมาชิก แต่เปิดเผยต่อสาธารณะเท่านั้นbegin()และตัวทำend()ซ้ำ - ด้วยการเข้าถึงโดยตรงเพื่อให้size()คุณสามารถreserveคัดลอกได้เร็วขึ้น แย่ลงยังคงถ้าพวกเขาเท่านั้นที่เปิดเผยat()และinsert()และอื่น ๆthrow....
  • คัดลอกการอ้างอิงกลับไปยังอ็อบเจ็กต์พาเรนต์ / การประสานงาน / การจัดการที่อาจไม่เป็นที่รู้จักหรือเขียนอย่างเดียวสำหรับโค้ดไคลเอ็นต์

2
ฉันคิดว่า 'ทำไม' ที่ใหญ่ที่สุดคือมันจะเป็นค่าใช้จ่ายรันไทม์ที่ยิ่งใหญ่ในการตรวจสอบว่าthis == otherทุกครั้งที่คุณเข้าถึงother.xซึ่งคุณจะต้องทำหากตัวปรับการเข้าถึงทำงานในระดับวัตถุ
aioobe

2
@aioobe ฉันคิดว่าคำตอบของคุณควรเป็นวิธีที่โดดเด่นมากขึ้น ในขณะที่คำตอบของ Tony นั้นดีและเป็นแนวคิด แต่ถ้าฉันเป็นนักพนันฉันจะพนันได้เลยว่าคำตอบของคุณคือเหตุผลทางประวัติศาสตร์ที่แท้จริงสำหรับการเลือก ไม่เพียง แต่มีประสิทธิภาพมากขึ้นเท่านั้น แต่ยังง่ายกว่ามากอีกด้วย คงเป็นคำถามที่ดีสำหรับ Bjarne!
Nir Friedman

ฉันทำเครื่องหมายคำตอบของคุณแล้วเพราะมันอธิบายเบื้องหลัง;)
demonking

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

@aioobe: ความคิดเห็นเก่า ๆ แต่อย่างไรก็ตาม ... "ตรวจสอบว่าthis == otherทุกครั้งที่คุณเข้าถึงother.x" - พลาดประเด็น - ถ้าother.xได้รับการยอมรับเฉพาะที่รันไทม์เมื่อเทียบเท่าthis.xจะไม่มีการเขียนตัวชี้มากนักother.xในตอนแรก คอมไพเลอร์อาจบังคับให้คุณเขียนif (this == other) ...this.x...สิ่งที่คุณกำลังจะทำ ของคุณ"ความสะดวกสบาย (มากยิ่งขึ้นหากตัวแปรเอกชนสาธารณะ)"คิดยังคิดถึงจุด - วิธีการมาตรฐานของการกำหนดข้อ จำกัด ก็พอที่จะอนุญาตให้เหมาะสม encapsulation แต่ไม่สะดวกไม่ได้โดยไม่จำเป็น
Tony Delroy

109

ปรับเปลี่ยนการเข้าถึงทำงานในระดับชั้นเรียนและไม่ได้อยู่ในระดับวัตถุ

นั่นคือสองวัตถุในคลาสเดียวกันสามารถเข้าถึงข้อมูลส่วนตัวของกันและกันได้

ทำไม:

สาเหตุหลักมาจากประสิทธิภาพ มันจะเป็นค่าใช้จ่ายรันไทม์ที่ไม่สำคัญเพื่อตรวจสอบว่าthis == otherทุกครั้งที่คุณเข้าถึงother.xซึ่งคุณจะต้องทำหากตัวปรับการเข้าถึงทำงานในระดับวัตถุ

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

และสะดวกอย่างเหลือเชื่อเมื่อเขียนตัวสร้างสำเนาและตัวดำเนินการกำหนด


35

คุณสามารถเข้าถึงสมาชิกส่วนตัวของชั้นเรียนได้จากในชั้นเรียนแม้กระทั่งสมาชิกของชั้นเรียนอื่น


10

เพื่อให้เข้าใจคำตอบฉันอยากจะเตือนคุณเกี่ยวกับแนวคิดบางประการ

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

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

หวังว่านี่จะตอบคำถามของคุณ


6

ตัวสร้างการคัดลอกเป็นฟังก์ชันสมาชิกของคลาสและด้วยเหตุนี้จึงสามารถเข้าถึงสมาชิกข้อมูลคลาสได้แม้กระทั่งผู้ที่ประกาศว่าเป็น 'ส่วนตัว'


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