std :: vector กับ std :: array ใน C ++


283

อะไรคือความแตกต่างระหว่าง a std::vectorและstd::arrayc ++ เมื่อใดที่หนึ่งควรจะชอบมากกว่าอีก ข้อดีและข้อเสียของแต่ละข้อคืออะไร? ตำราเรียนของฉันทั้งหมดคือรายการที่เหมือนกัน


1
ฉันกำลังมองหาการเปรียบเทียบstd::vectorกับstd::arrayและเงื่อนไขต่างกันอย่างไร
Zud

Zud std::arrayไม่เหมือนกับอาร์เรย์ C ++ std::arrayเป็น wrapper ที่บางมากรอบ ๆ อาร์เรย์ C ++ โดยมีวัตถุประสงค์หลักในการซ่อนตัวชี้จากผู้ใช้ของคลาส ฉันจะอัปเดตคำตอบของฉัน
ClosureCowboy

ฉันได้อัปเดตชื่อคำถามและข้อความเพื่อแสดงความกระจ่างของคุณ
Matteo Italia

คำตอบ:


318

std::vectorเป็นคลาสเทมเพลตที่แค็ปซูลอาเรย์แบบไดนามิก1 ที่เก็บในฮีปซึ่งจะเพิ่มและลดขนาดโดยอัตโนมัติหากมีการเพิ่มหรือลบองค์ประกอบ มันให้ hooks ทั้งหมด ( begin(),, end()iterators, ฯลฯ ) ที่ทำให้มันทำงานได้ดีกับ STL ที่เหลือ นอกจากนี้ยังมีวิธีการที่เป็นประโยชน์หลายอย่างที่ช่วยให้คุณสามารถดำเนินการที่อาร์เรย์ปกติจะยุ่งยากเช่นเช่นการแทรกองค์ประกอบที่อยู่ตรงกลางของเวกเตอร์

เนื่องจากมันเก็บองค์ประกอบไว้ในหน่วยความจำที่จัดสรรบนฮีปจึงมีโอเวอร์เฮดบางส่วนที่เกี่ยวข้องกับอาร์เรย์แบบคงที่

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

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

สำหรับการแนะนำให้std::arrayมีลักษณะที่บทความนี้ ; สำหรับการแนะนำที่รวดเร็วในการstd::vectorและการดำเนินงานที่มีความเป็นไปได้ในนั้นคุณอาจต้องการที่จะมองไปที่ของเอกสาร


  1. ที่จริงฉันคิดว่าในมาตรฐานพวกเขาอธิบายในแง่ของความซับซ้อนสูงสุดของการดำเนินการที่แตกต่างกัน (เช่นการเข้าถึงแบบสุ่มในเวลาคงที่การวนซ้ำขององค์ประกอบทั้งหมดในเวลาเชิงเส้นเพิ่มและลบองค์ประกอบที่สิ้นสุดในเวลาตัดจำหน่ายคงที่ ฯลฯ ) แต่ AFAIK ไม่มีวิธีการอื่นในการปฏิบัติตามข้อกำหนดดังกล่าวนอกเหนือจากการใช้อาร์เรย์แบบไดนามิก ตามที่ระบุไว้โดย @Lucretiel มาตรฐานจำเป็นต้องมีองค์ประกอบที่เก็บไว้อย่างต่อเนื่องดังนั้นจึงเป็นอาร์เรย์แบบไดนามิกที่เก็บไว้ที่ตัวจัดสรรที่เกี่ยวข้องวางไว้

6
เกี่ยวกับเชิงอรรถของคุณ: ในขณะที่เป็นจริงมาตรฐานยังรับรองว่าตัวชี้ทางคณิตศาสตร์ในองค์ประกอบภายในทำงานได้ซึ่งหมายความว่าจะต้องเป็นอาร์เรย์: & vec [9] - & vec [3] == 6 เป็นจริง
Lucretiel

9
ฉันค่อนข้างแน่ใจว่าเวกเตอร์นั้นไม่ลดขนาดอัตโนมัติ แต่เนื่องจาก C ++ 11 คุณสามารถโทร shrink_to_fit ได้
Dino

ฉันสับสนโดยสิ้นเชิงกับคำว่าstatic arrayและฉันไม่แน่ใจว่าคำศัพท์ที่ถูกต้องคืออะไร คุณหมายถึงอาร์เรย์ขนาดคงที่และไม่ใช่อาร์เรย์ตัวแปรแบบคงที่ (รายการหนึ่งใช้ที่เก็บข้อมูลแบบคงที่) stackoverflow.com/questions/2672085/… . คำศัพท์ที่ถูกต้องคืออะไร? อาร์เรย์แบบคงที่เป็นศัพท์ที่เลอะเทอะสำหรับอาร์เรย์ที่มีขนาดคงที่หรือไม่
Z boson

3
@Zboson: แน่นอนไม่ใช่แค่คุณคงเป็นคำที่ถูกทารุณกรรม staticคำสำคัญมากใน C ++ มีความหมายที่แตกต่างกันสามแบบและคำนี้มักใช้เพื่อพูดคุยเกี่ยวกับเนื้อหาที่ได้รับการแก้ไขในเวลารวบรวม ฉันหวังว่า "ขนาดคงที่" จะชัดเจนกว่านี้เล็กน้อย
Matteo Italia

3
สิ่งหนึ่งที่ต้องหมายเหตุ: สำหรับการเขียนโปรแกรมแบบ real-time (ที่คุณไม่ควรที่จะมีการใด ๆการจัดสรรแบบไดนามิก / deallocation หลังจากการเริ่มต้น) มาตรฐาน :: อาร์เรย์อาจจะเป็นที่ต้องการมากกว่ามาตรฐาน :: เวกเตอร์
TED

17

ใช้std::vector<T>คลาส:

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

  • ... ปรับขนาดโดยอัตโนมัติเมื่อแทรกองค์ประกอบใหม่

  • ... ช่วยให้คุณสามารถแทรกองค์ประกอบใหม่ที่จุดเริ่มต้นหรือในช่วงกลางของเวกเตอร์โดยอัตโนมัติ "เลื่อน" องค์ประกอบที่เหลือ "ขึ้น" โดยอัตโนมัติ (ที่เหมาะสมหรือไม่?) ช่วยให้คุณสามารถลบองค์ประกอบได้ทุกที่ในstd::vectorเช่นกันโดยอัตโนมัติลดองค์ประกอบที่เหลือลง

  • ... ช่วยให้คุณสามารถทำการอ่านค่าแบบตรวจสอบช่วงด้วยat()วิธีการ (คุณสามารถใช้ตัวจัด[]ทำดัชนีได้ตลอดเวลาหากคุณไม่ต้องการให้มีการตรวจสอบนี้)

มีสองสาม caveats หลักที่จะใช้std::vector<T>คือ:

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

  2. std::vector<bool>ชั้นโง่ มันใช้งานเป็นบิตฟิลด์แบบย่อไม่ใช่แบบอาเรย์ หลีกเลี่ยงมันถ้าคุณต้องการชุดbools!

  3. ในระหว่างการใช้งานstd::vector<T>s จะมีขนาดใหญ่กว่าอาร์เรย์ C ++ ที่มีจำนวนองค์ประกอบเท่ากัน นี่เป็นเพราะพวกเขาจำเป็นต้องติดตามข้อมูลอื่น ๆ จำนวนเล็กน้อยเช่นขนาดปัจจุบันของพวกเขาและเนื่องจากเมื่อใดก็ตามที่std::vector<T>มีการปรับขนาดพวกเขาจะจองพื้นที่เพิ่มเติมจากนั้นพวกเขาต้องการ เพื่อป้องกันไม่ให้มีการปรับขนาดทุกครั้งที่ใส่องค์ประกอบใหม่ พฤติกรรมนี้สามารถเปลี่ยนแปลงได้โดยการกำหนดเองallocatorแต่ฉันไม่เคยรู้สึกว่าต้องทำเช่นนั้น!


แก้ไข: หลังจากอ่านคำตอบของ Zud แล้วฉันรู้สึกว่าฉันควรจะเพิ่มสิ่งนี้:

std::array<T>ชั้นไม่ได้เป็นเช่นเดียวกับ C ++ อาร์เรย์ std::array<T>เป็น wrapper ที่บางมากรอบ ๆ C ++ arrays โดยมีวัตถุประสงค์หลักในการซ่อนตัวชี้จากผู้ใช้ของคลาส (ใน C ++, Array นั้นถูกใช้โดยปริยายว่าเป็นพอยน์เตอร์, เพื่อกำจัดผลกระทบ) std::array<T>ชั้นยังเก็บขนาดของมัน (ความยาว) ซึ่งจะมีประโยชน์มาก


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

4
สำหรับเวกเตอร์ที่ไม่ใช่บูลใน C ++ 11 และใหม่กว่าคุณสามารถโทรหาdata()a std::vector<T>เพื่อรับตัวชี้พื้นฐาน นอกจากนี้คุณยังสามารถใช้ที่อยู่ขององค์ประกอบ 0 (รับประกันว่าจะทำงานกับ C ++ 11, อาจทำงานได้กับเวอร์ชันก่อนหน้า)
Matt

16

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

  • "อาร์เรย์" ขนาดเล็ก (น้อยกว่า 100 องค์ประกอบพูดว่า) - (สแต็กทั่วไปประมาณ 8MB ดังนั้นอย่าจัดสรรเกินสองสาม KB ในสแต็กหรือน้อยกว่าหากโค้ดของคุณซ้ำ)
  • ขนาดจะได้รับการแก้ไข
  • อายุการใช้งานอยู่ในขอบเขตของฟังก์ชัน (หรือเป็นค่าสมาชิกที่มีอายุการใช้งานเท่ากับคลาสพาเรนต์)
  • คุณกำลังนับรอบ

ใช้std::arrayเวกเตอร์ทับอย่างแน่นอน ถ้าใด ๆ std::vectorของความต้องการเหล่านั้นไม่เป็นความจริงแล้วใช้


3
คำตอบที่ดี "เพื่อให้แน่ใจว่า std :: array ไม่ได้อยู่ใน stack เสมอมันขึ้นอยู่กับที่คุณจัดสรรมัน" ดังนั้นฉันจะสร้าง std :: array ไม่ได้บน stack ด้วยองค์ประกอบจำนวนมากได้อย่างไร
Trilarion

4
@Trilarion ใช้new std::arrayหรือทำให้มันเป็นสมาชิกของคลาสที่คุณใช้ 'ใหม่' เพื่อจัดสรร
Mark Lakata

ดังนั้นนี่หมายความว่าnew std::arrayยังคงคาดว่าจะรู้ขนาดของมันในเวลารวบรวมและไม่สามารถเปลี่ยนขนาดได้ แต่ยังคงอยู่บนกอง?
Trilarion

ใช่. ไม่มีประโยชน์ที่สำคัญในการใช้VSnew std::array new std::vector
Mark Lakata

12

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

รับการประกาศดังต่อไปนี้:

int cConc[3][5];
std::array<std::array<int, 5>, 3> aConc;
int **ptrConc;      // initialized to [3][5] via new and destructed via delete
std::vector<std::vector<int>> vConc;    // initialized to [3][5]

ตัวชี้ไปยังองค์ประกอบแรกในอาร์เรย์ c-style (cConc) หรือ std :: array (aConc) สามารถทำซ้ำผ่านทั้งอาร์เรย์ได้โดยเพิ่ม 1 ให้กับองค์ประกอบก่อนหน้า พวกเขาบรรจุแน่น

ตัวชี้ไปยังองค์ประกอบแรกในอาร์เรย์เวกเตอร์ (vConc) หรือตัวชี้อาร์เรย์ (ptrConc) สามารถทำซ้ำผ่านองค์ประกอบ 5 (ในกรณีนี้) แรกเท่านั้นจากนั้นมี 12 ไบต์ (บนระบบของฉัน) ของค่าใช้จ่ายสำหรับ เวกเตอร์ถัดไป

ซึ่งหมายความว่าอาร์เรย์ std :: vector> ที่เริ่มต้นเป็นอาร์เรย์ [3] [1000] จะมีขนาดเล็กกว่าในหน่วยความจำมากกว่าหนึ่งที่เริ่มต้นเป็นอาร์เรย์ [1000] [3] และทั้งคู่จะมีขนาดใหญ่กว่าในหน่วยความจำ std: อาร์เรย์จัดสรรวิธีใด

นอกจากนี้ยังหมายความว่าคุณไม่สามารถส่งผ่านอาร์เรย์เวกเตอร์หลายมิติ (หรือตัวชี้) ไปยังพูด OpenGL โดยไม่ต้องคำนึงถึงค่าใช้จ่ายของหน่วยความจำ แต่คุณสามารถส่งผ่าน std :: array หลายมิติไปยัง openGL ได้อย่างไร้เดียงสา


-17

เวกเตอร์เป็นคลาสคอนเทนเนอร์ในขณะที่อาร์เรย์เป็นหน่วยความจำที่จัดสรรไว้


23
คำตอบของคุณดูเหมือนว่าจะอยู่std::vector<T>กับT[]แต่คำถามเป็นเรื่องเกี่ยวกับstd::vector<T> std::array<T>
Keith Pinson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.