ควรใช้อาร์เรย์ใน C ++ หรือไม่?


97

เนื่องจากstd::listและstd::vectorมีอยู่มีเหตุผลที่จะใช้อาร์เรย์ C แบบดั้งเดิมใน C ++ หรือไม่หรือควรหลีกเลี่ยงเช่นเดียวกับmalloc?



18
@Als: คำถามนั้นเกี่ยวข้องกับความแตกต่างระหว่างคอนเทนเนอร์เฉพาะสองตู้ในขณะที่คำถามนี้เกี่ยวข้องกับความแตกต่างระหว่างอาร์เรย์ดิบและคอนเทนเนอร์มาตรฐานโดยทั่วไป
Jon Purdy

คำตอบ:


109

ใน C ++ 11 ที่std::arrayพร้อมใช้งานคำตอบคือ "ใช่อาร์เรย์ควรหลีกเลี่ยง" ก่อนหน้า C ++ 11 คุณอาจต้องใช้อาร์เรย์ C เพื่อจัดสรรอาร์เรย์ในที่จัดเก็บอัตโนมัติ (เช่นบนสแตก)


3
อย่างไรก็ตามคอมไพเลอร์จำนวนมากยังขาดการสนับสนุน C ++ 11 คุณจะต้องตัดสินใจว่าเมื่อใดควรใช้อย่างใดอย่างหนึ่งแทนที่จะใช้อย่างอื่นเนื่องจากไม่มี std :: array
Nowayz

4
std :: array เป็นเทมเพลตที่ส่งผลกระทบต่อโปรเจ็กต์ขนาดใหญ่ในแง่ของเวลาในการสร้างและอาจเป็นขนาดโค้ดเนื่องจากสำหรับการรวมกันของ T, N เทมเพลตจะถูกสร้างอินสแตนซ์ใหม่
zvrba

std :: vector รับประกันการจัดแนวข้อมูลตามมาตรฐานดังนั้นจึงสามารถใช้ได้เกือบทุกที่ ด้วย C ++ 11 ไม่มีเหตุผลที่จะใช้อาร์เรย์ C
Nils

16
อาร์เรย์ @Nils รับประกันการจัดตำแหน่งด้วย นอกจากนี้การจัดสรรพื้นที่เก็บข้อมูลอัตโนมัติ ("สแตก") ยังเร็วกว่าการจัดสรรพื้นที่เก็บข้อมูลแบบไดนามิก ถ้าฉันรู้ว่าฉันมีตรง 3 องค์ประกอบ [เช่นพิกัดของรูปสามเหลี่ยม] มีเหตุผลที่จะใช้เวกเตอร์ไม่มี
zvrba

9
@zvrba - ตรวจสอบชุดประกอบที่สร้างขึ้นเมื่อใช้ std :: array vs C arrays ไม่แตกต่างกันเลย
Nemanja Trifunovic

85

แน่นอนแม้ว่าจะมีstd::arrayใน C ++ 11 แต่ในทางปฏิบัติสำหรับข้อมูลคงที่เท่านั้น อาร์เรย์สไตล์ C มีข้อดีที่สำคัญสามประการ std::vectorดังนี้

  • ไม่ต้องการการจัดสรรแบบไดนามิก ด้วยเหตุนี้อาร์เรย์สไตล์ C จึงเป็นที่ต้องการซึ่งคุณน่าจะมีอาร์เรย์ขนาดเล็กจำนวนมาก พูดอะไรบางอย่างเช่นจุด n มิติ:

    template <typename T, int dims>
    class Point
    {
        T myData[dims];
    // ...
    };
    

    โดยทั่วไปแล้วเราอาจนึกภาพว่าdimsจะมีขนาดเล็กมาก (2 หรือ 3) Tเป็นชนิดในตัว ( double) และคุณอาจได้รับ std::vector<Point>องค์ประกอบหลายล้านรายการ แน่นอนว่าคุณไม่ต้องการการจัดสรรแบบไดนามิกจำนวน 3 คู่

  • การสนับสนุนการเริ่มต้นแบบคงที่ นี่เป็นเพียงปัญหาสำหรับข้อมูลคงที่ซึ่งบางสิ่งเช่น:

    struct Data { int i; char const* s; };
    Data const ourData[] =
    {
        { 1, "one" },
        { 2, "two" },
        //  ...
    };
    

    นี้มักจะเป็นที่นิยมในการใช้เวกเตอร์ (และstd::string) เพราะมันหลีกเลี่ยงทุกคำสั่งของการเริ่มต้นปัญหา; ข้อมูลถูกโหลดไว้ล่วงหน้าก่อนที่จะเรียกใช้โค้ดจริงใด ๆ

  • สุดท้ายที่เกี่ยวข้องกับข้างต้นคอมไพเลอร์สามารถคำนวณขนาดจริงของอาร์เรย์ได้จาก initializers คุณไม่จำเป็นต้องนับพวกเขา

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


11
การเริ่มต้นอาร์เรย์สไตล์ C ยังช่วยขจัดความจำเป็นในการทำซ้ำด้วยตัวคุณเอง ยังคงทำงานร่วมกับint i[] = { 1, 2, 3 }; ความต้องการที่จะเปลี่ยนแปลงตนเองเพื่อ int i[] = { 1, 2, 3, 4 };array<int, 3>array<int, 4>

10
@JoeWreschnig การเปลี่ยนแปลงที่คุณลืมได้ง่ายๆ หากคุณเพิ่มองค์ประกอบคอมไพลเลอร์ควรบ่น แต่ถ้าคุณลบออกคุณจะได้รับองค์ประกอบเริ่มต้น 0 รายการในตอนท้าย ฉันยังคงใช้อาร์เรย์สไตล์ C อย่างกว้างขวางสำหรับข้อมูลคงที่ประเภทนี้
James Kanze

3
ประโยคแรกไม่เข้าท่า
Konrad Rudolph

4
จุดที่สามสามารถแก้ไขได้อย่างสวยงามโดยใช้make_arrayฟังก์ชันคล้ายกับmake_pairฯลฯ Hat-tip ถึง@R มาตินเฟอร์นันเด
Konrad Rudolph

@KonradRudolph: แน่นอนค่ะ Andreas ถามว่า "ควรใช้อาร์เรย์ใน C ++ หรือไม่" ซึ่ง James ตอบว่า "แน่นอนแม้ว่าจะใช้std::arrayใน C ++ 11 ก็ตาม [ควรใช้] ในทางปฏิบัติสำหรับข้อมูลคงที่เท่านั้น"
Jon Purdy

15

อย่าพูดว่า "ไม่เคย" แต่ฉันยอมรับว่าบทบาทของพวกเขาลดลงอย่างมากจากโครงสร้างข้อมูลที่แท้จริงจาก STL

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


11

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


8
ก่อน C ++ 11 ฉันจะเห็นด้วย แต่std::array<T>จัดสรรบนสแต็กและโดยทั่วไปไม่มีค่าใช้จ่ายในอาร์เรย์ดิบ
111111

5
@ 111111 - เห็นด้วย แต่ฉันรู้ว่าบางคนในอุตสาหกรรมนั้นยังไม่ได้ย้ายไปที่ C ++ 11 เลย
Ed Heal

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

6
แต่ในระบบสำคัญด้านความปลอดภัยคุณจะไม่ใช้คุณสมบัติใหม่ของคอมไพเลอร์ (ผ่านการทดสอบน้อยกว่า) และคุณไม่ได้ใช้บูสต์
James Kanze

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

6

arrayในc++ช่วยให้คุณมีทางเลือกที่รวดเร็วขนาดคงที่สำหรับขนาดไดนามิกstd::vectorและstd::list. std :: arrayเป็นหนึ่งในส่วนเสริมในc++11. มันให้ประโยชน์ของคอนเทนเนอร์ std ในขณะที่ยังคงให้ความหมายประเภทรวมของอาร์เรย์สไตล์ C

ดังนั้นc++11ฉันจะใช้std::arrayในที่ที่จำเป็นอย่างแน่นอนทับเวกเตอร์ แต่ฉันจะหลีกเลี่ยงอาร์เรย์สไตล์ C ในC++03.


4

ส่วนใหญ่มักจะไม่vectorsฉันไม่สามารถคิดว่าเหตุผลที่จะใช้อาร์เรย์ดิบมากกว่าการพูด ถ้ารหัสใหม่ .

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


1
... แต่เนื่องจาก C ++ 03 เวกเตอร์ "มี" อาร์เรย์ซึ่งคุณสามารถเข้าถึงได้โดยใช้ตัวชี้เพื่ออ่านหรือเขียน ซึ่งครอบคลุมกรณีส่วนใหญ่ของโค้ดที่คาดว่าพอยน์เตอร์ไปยังอาร์เรย์ ก็ต่อเมื่อโค้ดนั้นจัดสรรหรือปลดปล่อยอาร์เรย์ที่คุณไม่สามารถใช้เวกเตอร์ได้
Steve Jessop

@SteveJessop คุณสามารถเข้าถึงอาร์เรย์ภายในได้หรือไม่?
Luchian Grigore

1
@LuchianGrigore: vector.data()ใน C ++ 11 หรือ&vector.front()ก่อนหน้านี้
Mike Seymour

@Luchian: หากเวกเตอร์ไม่ว่างคุณสามารถนำตัวชี้ไปที่องค์ประกอบ (และถ้าว่างคุณสามารถส่งตัวชี้ค่าว่างและความยาว 0 ไปยังฟังก์ชันที่เขียนอย่างสมเหตุสมผลใด ๆ ที่ยอมรับขอบกรณีของ a บัฟเฟอร์ขนาดศูนย์) จุดประสงค์เพียงอย่างเดียวของการรับประกันความต่อเนื่องของเวกเตอร์ที่เพิ่มเข้ามาใน C ++ 03 คือการอนุญาตให้ใช้เวกเตอร์เป็นบัฟเฟอร์โดยโค้ดที่เน้นตัวชี้
Steve Jessop

1
@SteveJessop และความจริงที่ว่าหลายคนคิดว่ามันรับประกันอยู่แล้วและก็ถือว่าดีกว่าที่จะไม่ทำให้พวกเขาผิดหวัง
James Kanze

4

ฉันรู้ว่าผู้คนจำนวนมากกำลังชี้ให้เห็น std :: array สำหรับการจัดสรรอาร์เรย์บนสแต็กและ std :: vector สำหรับ heap แต่ดูเหมือนจะไม่สนับสนุนการจัดตำแหน่งที่ไม่ใช่เนทีฟ หากคุณกำลังทำรหัสตัวเลขชนิดใด ๆ ที่คุณต้องการใช้คำแนะนำ SSE หรือ VPX (จึงต้องมีการจัดตำแหน่ง 128 หรือ 256 ไบต์ตามลำดับ) อาร์เรย์ C จะยังคงเป็นทางออกที่ดีที่สุดของคุณ


3

ฉันจะบอกว่าอาร์เรย์ยังคงมีประโยชน์ถ้าคุณจัดเก็บข้อมูลคงที่เพียงเล็กน้อยทำไมไม่


2

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


6
"เวกเตอร์ไม่สามารถผ่านการเป็นเจ้าของของข้อมูล" - ใช่มันสามารถใน C ++ 03 swapโดยใช้
Steve Jessop

2

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


3
อาร์เรย์สไตล์ C มีพื้นฐานมากกว่าstd::arrays อย่างไร ทั้งสองในหลาย ๆ กรณีจะถูกคอมไพล์เป็นแอสเซมบลีเดียวกัน
leftaround ประมาณ

1
พื้นฐานเพิ่มเติมที่เป็นพื้นฐานมากขึ้น คุณรู้ว่าอาร์เรย์กำลังจะทำอะไร std :: array อาจมีการใช้งานที่ผิดปกติเนื่องจากอาศัยไลบรารีมาตรฐาน
James Wynn

1
@JamesWynn ไม่ได้จริงๆ std::arrayได้กำหนดความหมายไว้อย่างแม่นยำซึ่งสร้างขึ้นที่ด้านบนของอาร์เรย์แบบคงที่
Konrad Rudolph

1

คุณควรใช้คอนเทนเนอร์ STL ภายใน แต่คุณไม่ควรส่งพอยน์เตอร์ไปยังคอนเทนเนอร์ดังกล่าวระหว่างโมดูลต่างๆมิฉะนั้นคุณจะต้องตกอยู่ในนรกแห่งการพึ่งพา ตัวอย่าง:

std::string foo;
//  fill foo with stuff
myExternalOutputProc(foo.c_str());

เป็นทางออกที่ดีมาก แต่ไม่ใช่

std::string foo;
//  fill foo with stuff
myExternalOutputProc(&foo);

เหตุผลก็คือ std :: string สามารถใช้งานได้หลายวิธี แต่สตริง c จะเป็นสตริงสไตล์ c เสมอ


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

เป็นเพียงคำแนะนำเมื่อต้องใช้อาร์เรย์หรือคอนเทนเนอร์ STL สร้างข้อมูลโดยใช้คอนเทนเนอร์ส่งผ่านเป็นอาร์เรย์ สำหรับข้อมูลอื่น ๆ ที่สตริงคุณจะมีบางอย่างเช่น myExternalOutputProc (foo.rawPointerGet (), foo.count ());
user877329

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

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