C ++: สมาร์ทพอยน์เตอร์, พอยน์เตอร์ชี้, ไม่มีพอยน์เตอร์? [ปิด]


48

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

คุณอาจพิจารณา

  • ความเป็นเจ้าของวัตถุ
  • สะดวกในการใช้
  • คัดลอกนโยบาย
  • เหนือศีรษะ
  • การอ้างอิงครบวงจร
  • แพลตฟอร์มเป้าหมาย
  • ใช้กับภาชนะบรรจุ

คำตอบ:


32

หลังจากลองวิธีการต่าง ๆ วันนี้ฉันพบว่าตัวเองสอดคล้องกับแนวทางสไตล์ Google C ++ :

ถ้าคุณต้องการซีแมนทิกส์ชี้นำ scoped_ptr ยอดเยี่ยม คุณควรใช้ std :: tr1 :: shared_ptr ภายใต้เงื่อนไขที่เฉพาะเจาะจงเช่นเมื่อจำเป็นต้องจัดวัตถุโดยคอนเทนเนอร์ STL คุณไม่ควรใช้ auto_ptr [ ... ]

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

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


14
วันนี้คุณอาจต้องการใช้ std :: unique_ptr แทน scoped_ptr
Klaim

24

ฉันยังทำตามรถไฟแห่งความคิด ฉันต้องการอธิบายอย่างชัดเจนว่า "ชั้นนี้เป็นสมาชิกของสมาชิกนี้" ตามความเหมาะสม

shared_ptrผมไม่ค่อยใช้ ถ้าฉันทำฉันจะใช้อย่างเสรีweak_ptrทุกครั้งที่ทำได้เพื่อให้ฉันสามารถจัดการมันได้เหมือนกับการจัดการกับวัตถุแทนที่จะเพิ่มจำนวนการอ้างอิง

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

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

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

ด้วยวิธีนี้เกม iPhone หนึ่งเกมที่ฉันทำงานสามารถdeleteโทรได้เพียงครั้งเดียวเท่านั้นและนั่นก็อยู่ใน Obj-C ถึง C ++ bridge ที่ฉันเขียน

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


1
สรุปยอดเยี่ยม จริงๆแล้วคุณหมายถึง shared_ptr ซึ่งต่างจากการพูดถึง smart_ptr ของคุณหรือไม่?
jmp97

ใช่ฉันหมายถึง shared_ptr ฉันจะแก้ไขมัน
Tetrad

10

ใช้เครื่องมือที่เหมาะสมสำหรับงาน

หากโปรแกรมของคุณสามารถโยนข้อยกเว้นให้แน่ใจว่ารหัสของคุณเป็นข้อยกเว้นทราบ การใช้พอยน์เตอร์อัจฉริยะ RAII และการหลีกเลี่ยงการสร้าง 2 เฟสนั้นเป็นจุดเริ่มต้นที่ดี

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

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

หากคุณทำงานในสภาพแวดล้อมแบบมัลติเธรดตรวจสอบให้แน่ใจว่าคุณเข้าใจว่าวัตถุของคุณอาจใช้ร่วมกันข้ามเธรด หนึ่งในเหตุผลหลักที่ควรพิจารณาใช้ boost :: shared_ptr หรือ std :: tr1 :: shared_ptr คือเนื่องจากมันใช้การนับจำนวนการอ้างอิงแบบเธรดที่ปลอดภัย

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

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

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

หากคุณใช้พอยน์เตอร์สมาร์ทเพียงเพื่อแชร์ทรัพยากรเช่นพื้นผิวหรือแบบจำลองให้พิจารณาไลบรารี่พิเศษเช่น Boost.Flyweight

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

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


3
การจัดการข้อยกเว้นบนคอนโซลอาจเป็นบิตหลบ - XDK โดยเฉพาะอย่างยิ่งคือการเรียงลำดับของข้อยกเว้นเป็นศัตรู
Crashworks

1
แพลตฟอร์มเป้าหมายควรมีอิทธิพลต่อการออกแบบของคุณจริงๆ ฮาร์ดแวร์ที่แปลงข้อมูลของคุณบางครั้งอาจมีอิทธิพลอย่างมากต่อซอร์สโค้ดของคุณ สถาปัตยกรรม PS3 เป็นตัวอย่างที่เป็นรูปธรรมที่คุณต้องใช้ฮาร์ดแวร์ในการออกแบบทรัพยากรและการจัดการหน่วยความจำของคุณเช่นเดียวกับ renderer ของคุณ
Simon

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

4

หากคุณกำลังใช้ C ++ 0x std::unique_ptr<T>ใช้

ไม่มีค่าใช้จ่ายด้านประสิทธิภาพstd::shared_ptr<T>ซึ่งต่างจากค่าอ้างอิงที่ใช้อ้างอิง unique_ptr เป็นเจ้าของตัวชี้ของตนและคุณสามารถโอนกรรมสิทธิ์รอบด้วยภาษา C ++ 0x ของความหมายย้าย คุณไม่สามารถคัดลอกพวกเขา - ย้ายพวกเขาเท่านั้น

นอกจากนี้ยังสามารถนำมาใช้ในภาชนะบรรจุเช่นstd::vector<std::unique_ptr<T>>ซึ่งเข้ากันได้กับไบนารีและเหมือนกันในประสิทธิภาพstd::vector<T*>แต่จะไม่รั่วไหลหน่วยความจำถ้าคุณลบองค์ประกอบหรือล้างเวกเตอร์ นี้ยังมีเข้ากันได้ดีกับขั้นตอนวิธี STL ptr_vectorกว่า

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


3

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

อย่างไรก็ตามเมื่อคุณต้องการติดตามทรัพยากรการผ่านพอยน์เตอร์เป็นเพียงตัวเลือกเดียว มีบางกรณี:

  • คุณได้รับพอยน์เตอร์จากที่อื่น แต่ไม่ต้องจัดการมัน: เพียงใช้พอยน์เตอร์ปกติและจัดทำเอกสารเพื่อไม่ให้ coder หลังจากที่คุณพยายามลบ
  • คุณได้รับตัวชี้จากที่อื่นและติดตามได้: ใช้ scoped_ptr
  • คุณได้รับตัวชี้จากที่อื่นและติดตามได้ แต่ต้องใช้วิธีพิเศษในการลบ: ใช้ shared_ptr ด้วยวิธีการลบแบบกำหนดเอง
  • คุณต้องการตัวชี้ในคอนเทนเนอร์ STL: มันจะถูกคัดลอกไปรอบ ๆ ดังนั้นคุณต้องเพิ่ม :: shared_ptr
  • หลายคลาสแบ่งใช้ตัวชี้และยังไม่ชัดเจนว่าใครจะลบมัน: shared_ptr (กรณีข้างต้นเป็นกรณีพิเศษของจุดนี้)
  • คุณสร้างตัวชี้ด้วยตัวคุณเองและเพียงคุณต้องการ: ถ้าคุณไม่สามารถใช้วัตถุปกติ: scoped_ptr
  • คุณสร้างตัวชี้และจะแบ่งปันกับคลาสอื่น ๆ : shared_ptr
  • คุณสร้างตัวชี้และส่งต่อ: ใช้ตัวชี้ปกติและจัดทำเอกสารอินเทอร์เฟซของคุณเพื่อให้เจ้าของใหม่รู้ว่าเขาควรจัดการทรัพยากรด้วยตัวเอง!

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


1

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

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

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

แก้ไข: มีบางสิ่งที่ต้องพิจารณาเกี่ยวกับประสิทธิภาพของตัวชี้ที่ใช้ร่วมกัน:

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

2
คุณสูญเสียฉันที่ 'หลีกเลี่ยงค่าใช้จ่ายทั้งหมด' จากนั้นคุณจะอธิบายประเภทของการเพิ่มประสิทธิภาพที่ไม่ค่อยเกี่ยวข้องกับเกมในโลกแห่งความเป็นจริง การพัฒนาเกมส่วนใหญ่มีลักษณะของปัญหาการพัฒนา (ความล่าช้าข้อบกพร่องความสามารถในการเล่นเป็นต้น) ไม่ใช่เพราะขาดแคชของ CPU ดังนั้นฉันจึงไม่เห็นด้วยอย่างยิ่งกับแนวคิดที่ว่าคำแนะนำนี้ไม่ใช่การเพิ่มประสิทธิภาพก่อนวัยอันควร
kevin42

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

1
นี่เป็นปัญหาที่ฉันเคยเห็นในสตูดิโอ AAA ที่ฉันทำงานอยู่ คุณสามารถฟังหัวหน้าสถาปนิกได้ที่ Insomniac Games, Mike Acton ฉันไม่ได้บอกว่าการเพิ่มประสิทธิภาพนั้นเป็นห้องสมุดที่ไม่ดีมันไม่เหมาะสำหรับเกมที่มีประสิทธิภาพสูง
Simon

1
@ kevin42: การเชื่อมโยงกันของแคชน่าจะเป็นแหล่งที่มาหลักของการเพิ่มประสิทธิภาพระดับต่ำในการพัฒนาเกมในปัจจุบัน @Simon: การใช้งาน shared_ptr ส่วนใหญ่หลีกเลี่ยงการล็อคในแพลตฟอร์มใด ๆ ที่สนับสนุนการเปรียบเทียบและการแลกเปลี่ยนซึ่งรวมถึงพีซี Linux และ Windows และฉันเชื่อว่ามี Xbox

1
@Joe Wreschnig: จริงแล้วแคชมิสยังมีแนวโน้มมากที่สุดแม้ว่าจะเป็นสาเหตุของการเริ่มต้นใช้งานตัวชี้ที่ใช้ร่วมกัน (คัดลอกสร้างจากตัวชี้ที่อ่อนแอเป็นต้น) L2 cache-miss บนพีซีสมัยใหม่นั้นเหมือน 200 รอบและบน PPC (xbox360 / ps3) มันสูงกว่า ด้วยเกมที่เข้มข้นคุณอาจมีวัตถุได้มากถึง 1,000 เกมเนื่องจากวัตถุแต่ละเกมสามารถมีทรัพยากรไม่มากนักเรากำลังมองหาประเด็นที่การปรับขนาดของพวกเขาเป็นเรื่องที่สำคัญ สิ่งนี้อาจทำให้เกิดปัญหาเมื่อสิ้นสุดรอบการพัฒนา (เมื่อคุณจะตีวัตถุเกมจำนวนมาก)
Simon

0

ฉันมักจะใช้พอยน์เตอร์อัจฉริยะทุกที่ ฉันไม่แน่ใจว่านี่เป็นความคิดที่ดีจริง ๆ หรือไม่ แต่ฉันขี้เกียจและฉันไม่สามารถเห็นข้อเสียที่แท้จริง [ยกเว้นว่าฉันต้องการทำตัวชี้แบบ C-style] ฉันใช้ boost :: shared_ptr เพราะฉันรู้ว่าฉันสามารถคัดลอกไปมาได้ - หากเอนทิตี้สองแห่งแชร์รูปภาพแล้วถ้าใครตายอีกคนก็ไม่ควรเสียรูปเช่นกัน

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


1
ฉันใช้ share_ptr เกือบทุกที่เหมือนกัน - แต่วันนี้ฉันลองคิดดูว่าฉันต้องการการเป็นเจ้าของร่วมกันสำหรับข้อมูลบางส่วนหรือไม่ หากไม่เป็นเช่นนั้นอาจเป็นเหตุผลที่ทำให้ข้อมูลนั้นเป็นสมาชิกที่ไม่ใช่ตัวชี้ไปยังโครงสร้างข้อมูลพาเรนต์ ฉันพบว่าเจ้าของชัดเจนลดความซับซ้อนของการออกแบบ
jmp97

0

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


0

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

แน่นอนที่ทำงานทุกอย่างแตกต่างกันยกเว้นว่าฉันเป็นคนต้นแบบซึ่งฉันต้องทำมาก


0

แทบจะไม่มีเลยแม้ว่าคำตอบนี้จะเป็นคำตอบที่แปลกและอาจจะไม่เหมาะกับทุกคน

แต่ฉันพบว่ามันมีประโยชน์มากกว่าในกรณีส่วนบุคคลของฉันในการจัดเก็บอินสแตนซ์ทุกประเภทโดยเฉพาะในลำดับส่วนกลางแบบสุ่มเข้าถึง (thread-safe) และแทนที่จะทำงานกับดัชนีแบบ 32 บิต (ที่อยู่ที่เกี่ยวข้องเช่น) แทนที่จะเป็นพอยน์เตอร์ที่แน่นอน

สำหรับการเริ่มต้น:

  1. มันลดความต้องการหน่วยความจำของตัวชี้แบบแอนะล็อกครึ่งหนึ่งบนแพลตฟอร์ม 64 บิต จนถึงตอนนี้ฉันไม่เคยต้องการอินสแตนซ์ของชนิดข้อมูลเฉพาะมากกว่า ~ 4.29 พันล้านรายการ
  2. ทำให้แน่ใจว่าอินสแตนซ์ทุกประเภทที่เจาะจงTจะไม่กระจัดกระจายในหน่วยความจำมากเกินไป ซึ่งมีแนวโน้มที่จะลดการพลาดแคชของรูปแบบการเข้าถึงทุกประเภทแม้กระทั่งการเชื่อมโยงโครงสร้างที่เชื่อมโยงเช่นต้นไม้หากโหนดเชื่อมต่อเข้าด้วยกันโดยใช้ดัชนีแทนที่จะเป็นพอยน์เตอร์
  3. ข้อมูลแบบขนานจะกลายเป็นเรื่องง่ายในการเชื่อมโยงโดยใช้อาร์เรย์แบบขนานราคาถูก (หรืออาร์เรย์แบบกระจาย) แทนที่จะเป็นต้นไม้หรือตารางแฮช
  4. Set intersections สามารถพบได้ใน linear-time หรือดีกว่าโดยใช้เป็นชุดบิตขนาน
  5. เราสามารถเรียงลำดับดัชนีและรับรูปแบบการเข้าถึงที่เป็นมิตรต่อแคช
  6. เราสามารถติดตามจำนวนอินสแตนซ์ที่มีการจัดสรรชนิดข้อมูลเฉพาะ
  7. ลดจำนวนสถานที่ที่ต้องจัดการกับสิ่งต่าง ๆ เช่นข้อยกเว้นด้านความปลอดภัยถ้าคุณสนใจเรื่องนั้น

ที่กล่าวถึงความสะดวกสบายเป็นข้อเสียเช่นเดียวกับความปลอดภัยของประเภท เราไม่สามารถเข้าถึงอินสแตนซ์ของTโดยไม่ต้องเข้าถึงทั้งคอนเทนเนอร์และดัชนี และคนแก่ธรรมดาไม่ได้int32_tบอกอะไรเราเกี่ยวกับประเภทข้อมูลที่อ้างถึงดังนั้นจึงไม่มีความปลอดภัยในการพิมพ์ เราอาจลองเข้าถึงการBarใช้ดัชนีFooโดยไม่ตั้งใจ เพื่อลดปัญหาที่สองฉันมักจะทำสิ่งนี้:

struct FooIndex
{
    int32_t index;
};

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

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

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

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

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

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

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