ฉันควรเก็บวัตถุทั้งหมดหรือตัวชี้ไปยังวัตถุในภาชนะบรรจุหรือไม่


162

การออกแบบระบบใหม่ตั้งแต่เริ่มต้น ฉันจะใช้ STL เพื่อจัดเก็บรายการและแผนที่ของวัตถุระยะยาว

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

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

ข้อเสียที่ชัดเจนมากสองอย่างในการเล่นกับพอยน์เตอร์: 1) ฉันต้องจัดการการจัดสรร / การจัดสรรคืนของวัตถุเหล่านี้ด้วยตัวเองในขอบเขตที่นอกเหนือจาก STL 2) ฉันไม่สามารถสร้างวัตถุชั่วคราวในสแต็กและเพิ่มลงในคอนเทนเนอร์ของฉัน

มีอะไรอีกบ้างที่ฉันขาดไป?


36
พระเจ้าที่ฉันรักเว็บไซต์นี้เป็นคำถามที่แน่นอนผมคิดว่าวันนี้ ... ขอบคุณสำหรับการทำผลงานของการขอให้ฉัน :-)
eviljack

2
อีกสิ่งที่น่าสนใจคือเราควรตรวจสอบว่ามีการเพิ่มตัวชี้ไปยังคอลเลกชันจริงหรือไม่และหากเราไม่ควรโทรลบเพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำ ... ถ้า ((set.insert (ตัวชี้)) second = false) {delete pointer;}
javapowered

คำตอบ:


68

เนื่องจากคนกำลังตีระฆังในประสิทธิภาพของการใช้ตัวชี้

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

อีกอย่าง, ถ้าการอัพเดทเป็นพอยน์เตอร์ทั่วไปในการจัดเก็บจะประหยัดต้นทุนการคัดลอก / ย้าย


7
ในแง่ของที่ตั้งแคชการจัดเก็บพอยน์เตอร์ลงในเวกเตอร์อาจมีประสิทธิภาพหากใช้พร้อมกับตัวจัดสรรที่กำหนดเองสำหรับพอยท์ ตัวจัดสรรที่กำหนดเองต้องดูแลตำแหน่งของแคชตัวอย่างเช่นการใช้ตำแหน่งใหม่ (ดูen.wikipedia.org/wiki/Placement_syntax#Custom_allocators )
amit

47

มันขึ้นอยู่กับสถานการณ์ของคุณ

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

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

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

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

class BigExpensive { ... }

// create a pointer vector
ptr_vector<BigExpensive> bigVector;
bigVector.push_back( new BigExpensive( "Lexus", 57700 ) );
bigVector.push_back( new BigExpensive( "House", 15000000 );

// get a reference to the first element
MyClass& expensiveItem = bigList[0];
expensiveItem.sell();

คลาสเหล่านี้ล้อมรอบคอนเทนเนอร์ STL และทำงานกับอัลกอริธึม STL ทั้งหมดซึ่งมีประโยชน์จริงๆ

นอกจากนี้ยังมีสิ่งอำนวยความสะดวกสำหรับการถ่ายโอนความเป็นเจ้าของตัวชี้ในคอนเทนเนอร์ไปยังผู้โทร (ผ่านฟังก์ชั่นการเปิดตัวในคอนเทนเนอร์ส่วนใหญ่)


38

หากคุณกำลังเก็บวัตถุ polymporhic คุณจำเป็นต้องใช้คอลเลกชันของพอยน์เตอร์คลาสพื้นฐานเสมอ

นั่นคือถ้าคุณวางแผนที่จะจัดเก็บประเภทที่แตกต่างกันในคอลเลกชันของคุณคุณจะต้องเก็บพอยน์เตอร์หรือกินโดย deamon หั่น


1
ฉันชอบ deamon ที่หั่นเป็นชิ้น!
idichekop

22

ขออภัยที่จะกระโดดใน 3 ปีหลังจากเหตุการณ์ แต่มีข้อควรระวังที่นี่ ...

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

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

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


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

คุณสามารถทิ้งรายการไว้ในเวกเตอร์ด้วยค่าซีแมนทิกส์และทำพฤติกรรมแบบ polymorphic ภายใน
Billy ONeal

19

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


วิธีการนี้แตกต่างจากสิ่งที่ Nick Haddad แนะนำโดยใช้ห้องสมุด Boost Pointer Container อย่างไร
Thorsten Schöning

10
@ ThorstenSchöning std :: shared_ptr ไม่ได้เพิ่มการพึ่งพา
James Johnston

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

11

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

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


4
มันไม่ได้มีประสิทธิภาพมากที่สุดถ้าวัตถุของคุณมีขนาดใหญ่และคุณย้ายองค์ประกอบไปมาบ่อยๆ
allyourcode

3

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

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

@ Torbjörnเป็นจุดที่ดีเกี่ยวกับการหั่น


1
Oh, และไม่เคย เคย เคยสร้างคอลเลกชันของ auto_ptr ของ
Torbjörn Gyllebring

ใช่ auto_ptr ไม่ใช่ตัวชี้อัจฉริยะ - มันไม่นับการอ้างอิง
Lou Franco

auto_ptr ไม่มีซีแมนทิกส์การคัดลอกที่ไม่ทำลายเช่นกัน การกระทำของการมอบหมายของ auto_ptr จากoneไปanotherจะปล่อยอ้างอิงจากและการเปลี่ยนแปลงone one
Andy Finkenstadt

3

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

มีข้อมูลที่เป็นประโยชน์เกี่ยวกับคอนเทนเนอร์ STL และตัวชี้สมาร์ทอยู่ที่นี่:

ทำไมการใช้ std :: auto_ptr <> กับคอนเทนเนอร์มาตรฐานจึงผิด


2

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

เช่น:

std::vector<boost::shared_ptr<protocol> > protocols;
...
connection c(protocols[0].get()); // pointer to protocol stays valid even if resized

หากไม่มีใครเก็บพอยน์เตอร์ไว้ที่วัตถุหรือรายการไม่ขยายและหดให้จัดเก็บเป็นวัตถุเก่าธรรมดา:

std::vector<protocol> protocols;
connection c(protocols[0]); // value-semantics, takes a copy of the protocol

1

คำถามนี้บักฉันมาระยะหนึ่งแล้ว

ฉันเอนตัวไปที่การเก็บพอยน์เตอร์ แต่ฉันมีข้อกำหนดเพิ่มเติมบางอย่าง (ห่อตัว SWIG lua) ที่อาจไม่เหมาะกับคุณ

จุดที่สำคัญที่สุดในโพสต์นี้คือการทดสอบด้วยตัวเองโดยใช้วัตถุของคุณ

ฉันทำสิ่งนี้ในวันนี้เพื่อทดสอบความเร็วในการเรียกฟังก์ชั่นสมาชิกในคอลเล็กชั่น 10 ล้านชิ้น 500 ครั้ง

ฟังก์ชันนี้อัพเดต x และ y ตาม xdir และ ydir (ตัวแปรสมาชิกแบบลอยทั้งหมด)

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

สำหรับการอ้างอิงด้วย -O3 บนฮาร์ดแวร์ของฉันพอยน์เตอร์ใช้เวลา 41 วินาทีในการดำเนินการและวัตถุดิบใช้เวลา 30 วินาทีในการดำเนินการให้เสร็จสมบูรณ์

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