ตัวชี้อัจฉริยะ: ใครเป็นเจ้าของวัตถุ [ปิด]


114

C ++ เป็นข้อมูลเกี่ยวกับหน่วยความจำที่เป็นเจ้าของ - aka ความหมายเป็นเจ้าของ

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

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

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

ดังนั้นคำถาม:

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

ให้เก็บประเภทการเป็นเจ้าของทางความหมายไว้ 1 ประเภทต่อคำตอบเพื่อให้สามารถโหวตขึ้นและลงทีละรายการได้

สรุป:

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

std::auto_ptr<T>:

คนเดียวเป็นเจ้าของวัตถุ อนุญาตให้โอนความเป็นเจ้าของได้

การใช้งาน: ช่วยให้คุณกำหนดอินเทอร์เฟซที่แสดงการโอนความเป็นเจ้าของอย่างชัดเจน

boost::scoped_ptr<T>

คนเดียวเป็นเจ้าของวัตถุ ไม่อนุญาตให้โอนความเป็นเจ้าของ

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

boost::shared_ptr<T>( std::tr1::shared_ptr<T>)

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

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

boost::weak_ptr<T>:

ใช้กับshared_ptr<T>สถานการณ์ที่อาจเกิดวงจรของพอยน์เตอร์

การใช้งาน: ใช้เพื่อหยุดรอบจากการรักษาวัตถุเมื่อมีเพียงรอบนั้นเท่านั้นที่รักษาจำนวนอ้างอิงที่ใช้ร่วมกัน


14
?? คำถามคืออะไร?
Pacerier

9
ฉันแค่อยากจะชี้ให้เห็นว่าเนื่องจากคำถามนี้ถูกโพสต์โดย auto_ptr ได้ถูกเลิกใช้เพื่อสนับสนุน unique_ptr (ซึ่งเป็นมาตรฐานในขณะนี้)
Juan Campa

In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good (IMO) สามารถเปลี่ยนวลีนี้ได้หรือไม่? ฉันไม่เข้าใจเลย
lolololol

@lololololol คุณตัดครึ่งประโยค In C++ ownership is documented by the type a RAW pointer is wrapped inside thus in a good C++ program it is very rare to see RAW pointers passed around. ตัวชี้ RAW ไม่มีความหมายความเป็นเจ้าของ หากคุณไม่รู้จักเจ้าของคุณไม่ทราบว่าใครเป็นผู้รับผิดชอบในการลบวัตถุมีคลาสมาตรฐานหลายคลาสที่ใช้ในการตัดพอยน์เตอร์ (std :: shared_ptr, std :: unique_ptr ฯลฯ ) ที่กำหนดความเป็นเจ้าของดังนั้น กำหนดว่าใครเป็นผู้รับผิดชอบในการลบตัวชี้
Martin York

1
ใน C ++ 11 + อย่าใช้ auto_ptr! ใช้ unique_ptr แทน!
val กล่าวว่า Reinstate Monica

คำตอบ:


20

สำหรับฉัน 3 ชนิดนี้ครอบคลุมความต้องการส่วนใหญ่ของฉัน:

shared_ptr - การอ้างอิงนับการจัดสรรตำแหน่งเมื่อตัวนับถึงศูนย์

weak_ptr- เหมือนกับข้างบน แต่เป็น "ทาส" สำหรับ a shared_ptrไม่สามารถจัดสรรปันส่วนได้

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

ฉันมีการใช้งานของตัวเองสำหรับสิ่งเหล่านี้ แต่ก็มีให้ในBoost.

ฉันยังคงส่งผ่านวัตถุโดยการอ้างอิง ( constทุกครั้งที่เป็นไปได้) ในกรณีนี้วิธีที่เรียกว่าต้องถือว่าวัตถุนั้นมีชีวิตอยู่ในช่วงเวลาที่โทรเท่านั้น

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


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

4
โดยพื้นฐานแล้วเป็นสัญญาการออกแบบเพื่อทำให้สิ่งต่างๆชัดเจน เมื่อวัตถุลูกได้รับ hub_ptr มันจะรู้ว่าวัตถุปลายแหลมจะไม่ถูกทำลายในช่วงชีวิตของเด็กและไม่มีความเป็นเจ้าของ ทั้งวัตถุที่บรรจุอยู่และวัตถุในภาชนะยอมรับกฎระเบียบที่ชัดเจน หากคุณใช้ตัวชี้เปล่ากฎสามารถจัดทำเป็นเอกสารได้ แต่จะไม่บังคับใช้โดยคอมไพเลอร์และโค้ด
Fabio Ceconello

1
นอกจากนี้โปรดทราบว่าคุณสามารถมี #ifdef เพื่อทำให้ hub_ptr ถูกพิมพ์เป็นตัวชี้เปล่าในรุ่นที่วางจำหน่ายดังนั้นค่าใช้จ่ายจะมีอยู่เฉพาะในรุ่นการแก้ปัญหาเท่านั้น
Fabio Ceconello

3
โปรดทราบว่าเอกสาร Boostขัดแย้งกับคำอธิบายของคุณเกี่ยวกับ scoped_ptr ระบุว่าเป็นnoncopyableและไม่สามารถโอนความเป็นเจ้าของได้
Alec Thomas

3
@ อเล็กซ์โทมัสคุณพูดถูก ฉันคิดเกี่ยวกับ auto_ptr และเขียน scoped_ptr การแก้ไข
Fabio Ceconello

23

โมเดล C ++ อย่างง่าย

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

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

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

  • ตัวชี้ดิบ
  • มาตรฐาน :: auto_ptr
  • เพิ่ม :: scoped_ptr

โมเดล C ++ ที่ชี้มาอย่างชาญฉลาด

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

  • เพิ่ม :: shared_ptr
  • เพิ่ม :: weak_ptr

ข้อสรุป

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


10

ไม่มีกรรมสิทธิ์ร่วม หากเป็นเช่นนั้นตรวจสอบให้แน่ใจว่าเป็นรหัสที่คุณไม่ได้ควบคุมเท่านั้น

วิธีนี้ช่วยแก้ปัญหาได้ 100% เนื่องจากมันบังคับให้คุณเข้าใจว่าทุกอย่างโต้ตอบอย่างไร


2
  • เจ้าของร่วมกัน
  • เพิ่ม :: shared_ptr

เมื่อทรัพยากรถูกแบ่งใช้ระหว่างหลายวัตถุ บูสต์ shared_ptr ใช้การนับอ้างอิงเพื่อให้แน่ใจว่าทรัพยากรไม่ได้รับการจัดสรรเมื่อทุกคนได้รับการแก้ไข


2

std::tr1::shared_ptr<Blah> มักเป็นทางออกที่ดีที่สุดของคุณ


2
shared_ptr เป็นเรื่องปกติมากที่สุด แต่ยังมีอีกมากมาย แต่ละคนมีรูปแบบการใช้งานของตัวเองและสถานที่ที่ดีและไม่ดีในการฟ้องร้อง คำอธิบายเพิ่มเติมจะดีกว่า
Martin York

หากคุณติดอยู่กับคอมไพเลอร์รุ่นเก่า boost :: shared_ptr <blah> คือสิ่งที่ std :: tr1 :: shared_ptr <blah> ขึ้นอยู่กับ เป็นคลาสที่ง่ายพอที่คุณอาจจะริพจาก Boost และใช้งานได้แม้ว่าคอมไพเลอร์ของคุณจะไม่รองรับ Boost เวอร์ชันล่าสุดก็ตาม
Branan

2

จากการเพิ่มยังมีไลบรารีคอนเทนเนอร์ตัวชี้ สิ่งเหล่านี้มีประสิทธิภาพและใช้งานง่ายกว่าคอนเทนเนอร์มาตรฐานของพอยน์เตอร์อัจฉริยะหากคุณจะใช้วัตถุในบริบทของคอนเทนเนอร์เท่านั้น

บน Windows มีตัวชี้ COM (IUnknown, IDispatch และเพื่อน) และตัวชี้อัจฉริยะต่างๆสำหรับจัดการ (เช่นCComPtrของ ATL และตัวชี้อัจฉริยะที่สร้างขึ้นโดยอัตโนมัติโดยคำสั่ง "นำเข้า" ใน Visual Studio ตามคลาส_com_ptr )


1
  • เจ้าของคนเดียว
  • เพิ่ม :: scoped_ptr

เมื่อคุณต้องการจัดสรรหน่วยความจำแบบไดนามิก แต่ต้องการให้แน่ใจว่าได้รับการจัดสรรทุกจุดออกของบล็อก

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


1

ฉันไม่คิดว่าฉันเคยเป็นเจ้าของร่วมกันในการออกแบบของฉัน ในความเป็นจริงจากด้านบนศีรษะของฉันมีเพียงกรณีเดียวที่ฉันคิดได้คือรูปแบบฟลายเวท


1

yasper :: ptr มีน้ำหนักเบาเพิ่ม :: shared_ptr เหมือนทางเลือก มันทำงานได้ดีในโครงการขนาดเล็ก (สำหรับตอนนี้) ของฉัน

ในหน้าเว็บที่http://yasper.sourceforge.net/มีคำอธิบายดังนี้:

ทำไมต้องเขียนตัวชี้สมาร์ท C ++ อื่น มีการใช้งานตัวชี้สมาร์ทคุณภาพสูงหลายตัวสำหรับ C ++ อยู่แล้วโดยส่วนใหญ่คือ Boost pointer pantheon และ SmartPtr ของ Loki สำหรับการเปรียบเทียบที่ดีของการใช้งานตัวชี้อัจฉริยะและเมื่อการใช้งานเหมาะสมโปรดอ่าน The New C ++: Smart (er) ของ Herb Sutter ตรงกันข้ามกับคุณสมบัติที่กว้างขวางของไลบรารีอื่น ๆ Yasper เป็นตัวชี้การนับอ้างอิงที่เน้นแคบ มันสอดคล้องกับนโยบาย shared_ptr ของ Boost และนโยบาย RefCounted / AllowConversion ของ Boost Yasper ช่วยให้โปรแกรมเมอร์ C ++ ลืมเกี่ยวกับการจัดการหน่วยความจำโดยไม่ต้องแนะนำการอ้างอิงขนาดใหญ่ของ Boost หรือต้องเรียนรู้เกี่ยวกับเทมเพลตนโยบายที่ซับซ้อนของ Loki ปรัชญา

* small (contained in single header)
* simple (nothing fancy in the code, easy to understand)
* maximum compatibility (drop in replacement for dumb pointers)

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


1

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

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

(เป็นการอ้างอิงที่ชาญฉลาดแทนที่จะเป็นตัวชี้อัจฉริยะเพราะคุณไม่จำเป็นต้องอ้างอิงอย่างชัดเจนเพื่อให้ได้เนื้อหา)

ซึ่งหมายความว่า auto_ptr มีความจำเป็นน้อยลง - จำเป็นต้องเติมช่องว่างที่ประเภทไม่มีข้อดีเท่านั้น swapฟังก์ชันเท่านั้น แต่คอนเทนเนอร์มาตรฐานทั้งหมดทำ


บางทีมันอาจจะไม่จำเป็น (ฉันจะบอกว่า scoped_ptr ทำให้มันมีความจำเป็นน้อยกว่านี้) แต่มันจะไม่หายไป การมีฟังก์ชั่น swap ไม่ได้ช่วยอะไรคุณเลยหากคุณจัดสรรบางอย่างบนฮีปและมีคนขว้างก่อนที่คุณจะลบหรือคุณลืมไป
Michel

นั่นคือสิ่งที่ฉันพูดในย่อหน้าสุดท้าย
Daniel Earwicker

0
  • เจ้าของคนเดียว: Aka delete on Copy
  • มาตรฐาน :: auto_ptr

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

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