ฉันจะใช้คอนเทนเนอร์ STL ใดในสถานการณ์ใด


185

ฉันอ่านค่า STL ในหนังสือของฉันบน C ++ โดยเฉพาะในส่วนของ STL และคอนเทนเนอร์ ตอนนี้ฉันเข้าใจแต่ละคนทุกคนมีคุณสมบัติเฉพาะของตัวเองและฉันใกล้จะจดจำพวกเขาทั้งหมด ... แต่สิ่งที่ฉันยังไม่เข้าใจคือสิ่งที่พวกเขาใช้

คำอธิบายคืออะไร รหัสตัวอย่างเป็นที่ต้องการมาก


คุณหมายถึง map vectot set ฯลฯ หรือเปล่า
โทมัส Tempelmann

แม้แต่การดูที่แผนภาพนี้ฉันไม่สามารถพูดได้ว่าอะไรจะดีที่สุดที่จะใช้ในการทดสอบstackoverflow.com/questions/9329011/…
sergiol

2
@sbi: การลบแท็ก C ++ Faq ออกจากแท็กนี้และเพิ่มลงใน C ++ 11 ที่รวมอยู่ล่าสุดฉันจะเลือกคอนเทนเนอร์ไลบรารีมาตรฐานใน C ++ 11 ได้อย่างมีประสิทธิภาพได้อย่างไร
Alok บันทึก

คำตอบ:


338

สูตรชีทนี้ให้ข้อสรุปที่ดีเกี่ยวกับภาชนะบรรจุที่แตกต่างกัน

ดูผังงานที่ด้านล่างเป็นแนวทางที่จะใช้ในสถานการณ์การใช้งานที่แตกต่างกัน:

http://linuxsoftware.co.nz/containerchoice.png

สร้างโดยDavid Mooreและได้รับลิขสิทธิ์ CC BY-SA 3.0


14
แผนผังลำดับงานนี้เป็นสีทองฉันหวังว่าฉันจะมีบางอย่างเช่นนี้ใน c #
Bruno

2
การเชื่อมโยง Updated: c ++ คอนเทนเนอร์โกงแผ่น
Bill Door

3
จุดเริ่มต้นจะต้องvectorค่อนข้างว่างเปล่า stackoverflow.com/questions/10699265/…
eonil

5
ตอนนี้คุณมีunordered_mapและunordered_set(และตัวแปรหลายตัว) ซึ่งไม่ได้อยู่ในแผนภูมิการไหล แต่เป็นตัวเลือกที่ดีเมื่อคุณไม่สนใจคำสั่งซื้อ แต่ต้องการค้นหาองค์ประกอบตามปุ่ม การค้นหาของพวกเขามักจะเป็น O (1) แทน O (log n)
Aidiakapi

2
@ shuttle87 ไม่เพียง แต่ขนาดนั้นจะไม่แปรผัน แต่ที่สำคัญกว่านั้นขนาดจะถูกกำหนดในเวลารวบรวมและจะไม่แปรผัน
YoungJohn

188

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

ป้อนคำอธิบายรูปภาพที่นี่


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

1
นี่เป็นแผนภูมิที่ยอดเยี่ยม แม้ว่าบางคนสามารถอธิบายให้ฉันได้ว่า 'ตำแหน่งถาวร' มีความหมายอย่างไร
IDDQD

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

1
นี่เป็นแผนภูมิที่ยอดเยี่ยมอย่างแท้จริง แต่ฉันคิดว่าvector (sorted)ค่อนข้างไม่สอดคล้องกับส่วนที่เหลือ มันไม่ได้เป็นประเภทที่แตกต่างกันของภาชนะบรรจุที่เหมือนกันstd::vectorแต่เรียงลำดับ สิ่งสำคัญยิ่งกว่านั้นคือฉันไม่เห็นว่าทำไมจึงไม่สามารถใช้ a std::setซ้ำสำหรับการสั่งซื้อได้หากเป็นพฤติกรรมมาตรฐานของการวนซ้ำชุด แน่นอนว่าถ้าคำตอบนั้นพูดถึงการเข้าถึงค่าของรางคอนเทนเนอร์อย่างเป็นระเบียบ[]คุณก็สามารถทำได้โดยใช้ soted std::vectorเท่านั้น แต่ในกรณีใดกรณีหนึ่งการตัดสินใจควรทำหลังจากคำถาม "จำเป็นต้องมีคำสั่งซื้อ"
59

1
@ user2019840 ฉันต้องการ จำกัด แผนภูมิเฉพาะคอนเทนเนอร์มาตรฐาน สิ่งที่ควรปรากฏแทนที่ "เวกเตอร์ที่จัดเรียง" คือ "flat_set" (จากBoost.Container ) หรือเทียบเท่า (ทุกไลบรารีหลักหรือรหัสฐานมีการเทียบเท่า flat_set AFAIK) แต่สิ่งเหล่านี้ไม่ได้มาตรฐานและค่อนข้างจะมองข้าม STL และสาเหตุที่คุณไม่ต้องการวนซ้ำผ่าน std :: set หรือ std :: map (อย่างน้อยก็ไม่บ่อย) คือมันไม่มีประสิทธิภาพมากนัก
Mikael Persson

41

คำตอบง่ายๆ: ใช้std::vectorสำหรับทุกสิ่งเว้นแต่ว่าคุณมีเหตุผลที่แท้จริงที่จะทำอย่างอื่น

เมื่อคุณพบกรณีที่คุณคิดว่า "Gee std::vectorทำงานได้ไม่ดีที่นี่เพราะ X" ให้ลองใช้พื้นฐานของ X


1
แต่ .. ต้องระวังไม่ให้ลบ / ใส่รายการเมื่อ iterating ... ใช้ const_iterator เท่าที่เป็นไปได้ที่จะหลีกเลี่ยงปัญหานี้ ..
vrdhn

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

13
@ Black Point คือเวกเตอร์มักจะเร็วกว่าแม้ในการปฏิบัติการที่ในทางทฤษฎีควรทำงานช้าลง
Bartek Banachewicz

1
@Vardhan std::remove_ifนั้นเหนือกว่า "วิธีการลบระหว่างการทำซ้ำ" เกือบทุกครั้ง
fredoverflow

1
เกณฑ์มาตรฐานบางอย่างจะช่วยให้การสนทนานี้เป็นไปได้น้อยกว่า
เฟลิกซ์ดี.

11

ดู STL ที่มีประสิทธิภาพโดย Scott Meyers มันเป็นการดีที่จะอธิบายวิธีการใช้ STL

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

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

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

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

มันไม่ได้อยู่ใน STL แต่อยู่ในการอัปเดต TR1 เป็น STL: ถ้าคุณมีคู่ของคีย์ - ค่าจำนวนมากที่คุณจะต้องค้นหาด้วยคีย์และคุณไม่สนใจคำสั่งของพวกเขาคุณอาจ ต้องการใช้แฮ - ซึ่งเป็น tr1 :: unordered_map ฉันใช้กับ Visual C ++ 7.1 ซึ่งเรียกว่า stdext :: hash_map มันมีการค้นหา O (1) แทนการค้นหา O (log n) สำหรับแผนที่


ฉันได้ยินมาว่ามีเกร็ดเล็กเกร็ดน้อยในตอนนี้แนะนำว่า Microsoft hash_mapไม่ได้ใช้งานได้ดี unordered_mapฉันหวังว่าพวกเขาได้ดีขึ้นใน
Mark Ransom

3
จากรายการ - "คุณไม่สามารถเข้าถึงองค์ประกอบ" - ฉันคิดว่าคุณหมายถึงคุณไม่สามารถเข้าถึงแบบสุ่มหรือจัดทำดัชนีโดยตรงกับองค์ประกอบ ....
Tony Delroy

^ ใช่เพราะการเข้าถึงตามลำดับเป็นสิ่งที่listทำ ค่อนข้างผิดพลาดที่เห็นได้ชัดมี
underscore_d

7

ฉันออกแบบผังงานใหม่ให้มี 3 คุณสมบัติ:

  1. ฉันคิดว่าคอนเทนเนอร์ STL แบ่งออกเป็น 2 คลาสหลัก คอนเทนเนอร์พื้นฐานและคอนเทนเนอร์พื้นฐานใช้ประโยชน์จากนโยบาย
  2. ในตอนแรกผังงานควรแบ่งขั้นตอนการตัดสินใจกับสถานการณ์หลักที่เราควรตัดสินใจและทำอย่างละเอียดในแต่ละกรณี
  3. คอนเทนเนอร์แบบขยายบางตัวมีความเป็นไปได้ในการเลือกคอนเทนเนอร์พื้นฐานที่แตกต่างกันเป็นคอนเทนเนอร์ภายใน Flowchart ควรพิจารณาสถานการณ์ที่สามารถใช้แต่ละคอนเทนเนอร์พื้นฐานได้

แผนผังลำดับงาน: ป้อนคำอธิบายรูปภาพที่นี่

ข้อมูลเพิ่มเติมที่มีอยู่ในลิงค์นี้


5

จุดสำคัญที่กล่าวถึงเพียงสั้น ๆ เพื่อให้ห่างไกลคือว่าถ้าคุณจำเป็นต้องมีหน่วยความจำที่อยู่ติดกัน (เช่นอาร์เรย์ C ช่วยให้) แล้วคุณสามารถใช้vector, หรือarraystring

ใช้arrayถ้าขนาดเป็นที่รู้จักกันในเวลารวบรวม

ใช้stringหากคุณต้องการทำงานกับประเภทอักขระและต้องการสตริงไม่ใช่คอนเทนเนอร์ที่ใช้งานทั่วไป

ใช้vectorในกรณีอื่น ๆ ทั้งหมด ( vectorควรเป็นตัวเลือกเริ่มต้นของคอนเทนเนอร์ในกรณีส่วนใหญ่อยู่แล้ว)

ด้วยสามสิ่งเหล่านี้คุณสามารถใช้data()ฟังก์ชันสมาชิกเพื่อรับตัวชี้ไปยังองค์ประกอบแรกของคอนเทนเนอร์


3

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

vector: เค้าโครงที่กะทัดรัดพร้อมค่าใช้จ่ายหน่วยความจำน้อยหรือไม่มีเลยต่อวัตถุที่มีอยู่ มีประสิทธิภาพในการย้ำมากกว่า ผนวกแทรกและลบอาจมีราคาแพงโดยเฉพาะอย่างยิ่งสำหรับวัตถุที่ซับซ้อน ราคาถูกเพื่อหาวัตถุที่มีอยู่โดยดัชนีเช่น myVector [10] ใช้ที่ที่คุณจะได้ใช้อาร์เรย์ใน C ดีที่คุณมีวัตถุง่าย ๆ (เช่น int) อย่าลืมที่จะใช้reserve()ก่อนที่จะเพิ่มวัตถุจำนวนมากในภาชนะ

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

set(และmultiset): โอเวอร์เฮดหน่วยความจำที่สำคัญต่อวัตถุที่มีอยู่ ใช้ที่ที่คุณต้องการค้นหาอย่างรวดเร็วหากคอนเทนเนอร์นั้นมีวัตถุที่กำหนดหรือรวมคอนเทนเนอร์อย่างมีประสิทธิภาพ

map(และmultimap): โอเวอร์เฮดหน่วยความจำที่สำคัญต่อวัตถุที่มีอยู่ ใช้ตำแหน่งที่คุณต้องการเก็บคู่คีย์ - ค่าและค้นหาค่าตามคีย์อย่างรวดเร็ว

แผนภูมิการไหลบนแผ่นโกงที่แนะนำโดย zdan จะให้แนวทางที่ครบถ้วน


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

ฉันจะยังคงนับสองพอยน์เตอร์ต่อวัตถุที่จัดเก็บเป็น "เล็ก"
เสนอราคา

เทียบกับอะไร std :: forward_list เป็นคอนเทนเนอร์ที่ส่วนใหญ่แนะนำให้มีการจัดเก็บข้อมูล meta น้อยต่อวัตถุ (เพียงหนึ่งตัวชี้) ในขณะที่ std :: vector เก็บข้อมูล 0 เมตาต่อวัตถุ ดังนั้น 2 พอยน์เตอร์จึงไม่สามารถต่อรองได้เมื่อเทียบกับตู้คอนเทนเนอร์อื่น ๆ
ฮันนาคาลิล

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

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

2

บทเรียนหนึ่งที่ฉันได้เรียนรู้คือพยายามห่อมันในชั้นเรียนเนื่องจากการเปลี่ยนประเภทภาชนะหนึ่งวันที่ดีอาจทำให้ประหลาดใจใหญ่

class CollectionOfFoo {
    Collection<Foo*> foos;
    .. delegate methods specifically 
}

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

มาเพื่อเลือกโครงสร้างข้อมูลที่สมบูรณ์แบบสำหรับงาน:

แต่ละโครงสร้างข้อมูลให้การดำเนินการบางอย่างซึ่งสามารถเปลี่ยนแปลงความซับซ้อนของเวลา:

O (1), O (lg N), O (N), เป็นต้น

คุณต้องคาดเดาอย่างดีที่สุดว่าการดำเนินการใดจะได้ผลดีที่สุดและใช้โครงสร้างข้อมูลที่มีการดำเนินการนั้นเป็น O (1)

ง่ายใช่มั้ย (-:


5
นี่ไม่ใช่เหตุผลที่เราใช้ตัววนซ้ำ?
ทองคำขาว Azure

@PlatinumAzure แม้ตัววนซ้ำควรเป็น typedef ของสมาชิก .. หากคุณเปลี่ยนประเภทคอนเทนเนอร์คุณต้องไปและเปลี่ยนคำจำกัดความตัววนซ้ำทั้งหมด ... สิ่งนี้ไม่ได้รับการแก้ไขใน c ++ 1x!
vrdhn

4
สำหรับคนที่อยากรู้อยากเห็นนี่คือการแก้ไขใน C ++ 11:auto myIterator = whateverCollection.begin(); // <-- immune to changes of container type
ดำ

1
จะtypedef Collection<Foo*> CollectionOfFoo;เพียงพอหรือไม่
Craig McQueen

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

1

ฉันขยายผังงานที่ยอดเยี่ยมของ Mikael Persson ฉันเพิ่มหมวดหมู่คอนเทนเนอร์บางส่วนคอนเทนเนอร์อาเรย์และหมายเหตุบางรายการ หากคุณต้องการสำเนาของคุณเองนี่คือ Google Drawing ขอบคุณมิคาเอลที่ทำรากฐาน! ตัวเลือกคอนเทนเนอร์ C ++


1

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

ตามที่ @David Thornley ตอบ std :: vector เป็นหนทางที่จะไปหากไม่มีความต้องการพิเศษอื่น ๆ นี่คือคำแนะนำที่ได้รับจากผู้สร้าง C ++, Bjarne Stroustrup ในบล็อก 2014

นี่คือลิงค์สำหรับบทความ https://isocpp.org/blog/2014/06/stroustrup-lists

และอ้างจากที่หนึ่ง

และใช่คำแนะนำของฉันคือการใช้ std :: vector โดยค่าเริ่มต้น

ในความคิดเห็นผู้ใช้ @NathanOliver ยังมีบล็อกที่ดีอีกอันหนึ่งซึ่งมีการวัดที่เป็นรูปธรรมมากขึ้น https://baptiste-wicht.com/posts/2012/12/cpp-benchmark-vector-list-deque.html

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