std :: องค์ประกอบเวกเตอร์รับประกันว่าจะติดกันหรือไม่


111

คำถามของฉันง่ายมาก: มีการรับประกันว่าองค์ประกอบของเวกเตอร์ std :: ติดกันหรือไม่ ตามลำดับคำฉันสามารถใช้ตัวชี้ไปยังองค์ประกอบแรกของ std :: vector เป็น C-array ได้หรือไม่

หากหน่วยความจำของฉันทำงานได้ดีมาตรฐาน C ++ ก็ไม่ได้รับประกันเช่นนั้น อย่างไรก็ตามข้อกำหนด std :: vector นั้นแทบจะเป็นไปไม่ได้เลยที่จะตอบสนองความต้องการเหล่านั้นหากองค์ประกอบไม่ติดกัน

ใครสามารถชี้แจงเรื่องนี้ได้บ้าง?

ตัวอย่าง:

std::vector<int> values;
// ... fill up values

if( !values.empty() )
{
    int *array = &values[0];
    for( int i = 0; i < values.size(); ++i )
    {
        int v = array[i];
        // do something with 'v'
    }
}

ฉันรู้ว่าคุณกำลังมีปัญหาถ้าคุณกลายพันธุ์valuesในifบล็อกนั้น ฉันไม่รู้คำตอบสำหรับคำถามของคุณดังนั้นฉันแค่แสดงความคิดเห็น :)
Greg D

@Greg: มีปัญหาอะไร - คุณสามารถอธิบายเล็กน้อยได้หรือไม่?
Reunanen

ฉันคิดว่าเขาหมายความว่าการกดค่าใหม่อาจทำให้เกิด "การจัดสรรซ้ำ" ซึ่งจะทำให้อาร์เรย์ไม่ถูกต้อง
Martin Cote

การเรียกที่กลายพันธุ์valuesโดยเฉพาะว่าเปลี่ยนขนาดของมัน (เช่นpush_back()) อาจแจ้งให้มีการจัดสรรเวกเตอร์ที่อยู่ภายใต้ใหม่ซึ่งทำให้ตัวชี้ที่คัดลอกไปไม่ถูกarrayต้อง เป็นหลักการเดียวกันกับที่ใช้ vector :: iterator แทนตัวชี้ในเวกเตอร์ :)
Greg D

1
ใช่ฉันใส่ค่า `` รอบตัวเพื่อพยายามทำให้ชัดเจนว่าฉันกำลังพูดถึงคลาสนั้นเองไม่ใช่ค่าที่อยู่ในนั้น :) การตั้งชื่อที่ไม่ดีและทั้งหมดนั้น ฉันไม่คิดว่ามันจะเป็นปัญหาจริงๆในกรณีทั่วไปที่คำถามนี้เกี่ยวข้อง - ทำไมบางคนถึงดึงตัวชี้ไปยังหน่วยความจำจากนั้นเริ่มยุ่งกับเวกเตอร์แทนที่จะใช้ตัวชี้? ความโง่.
Greg D

คำตอบ:


118

สิ่งนี้พลาดจากมาตรฐาน C ++ 98 ที่เหมาะสม แต่ต่อมาถูกเพิ่มเป็นส่วนหนึ่งของ TR แน่นอนว่ามาตรฐาน C ++ 0x ที่กำลังจะมาถึงจะมีสิ่งนี้เป็นข้อกำหนด

จาก n2798 (ร่าง C ++ 0x):

23.2.6 เวกเตอร์แม่แบบคลาส [เวกเตอร์]

1 เวกเตอร์เป็นคอนเทนเนอร์ลำดับที่รองรับตัววนซ้ำการเข้าถึงโดยสุ่ม นอกจากนี้ยังรองรับการแทรกเวลาคงที่ (ตัดจำหน่าย) และลบการดำเนินการในตอนท้าย แทรกและลบตรงกลางใช้เวลาเชิงเส้น การจัดการพื้นที่จัดเก็บจะได้รับการจัดการโดยอัตโนมัติแม้ว่าจะสามารถให้คำแนะนำเพื่อปรับปรุงประสิทธิภาพได้ องค์ประกอบของเวกเตอร์จะถูกจัดเก็บอย่างต่อเนื่องซึ่งหมายความว่าถ้า v เป็นเวกเตอร์โดยที่ T เป็นประเภทอื่นที่ไม่ใช่บูลมันจะเป็นไปตามเอกลักษณ์ & v [n] == & v [0] + n สำหรับทั้งหมด 0 <= n <v .ขนาด().


3
นอกจากนี้ยังระบุไว้ใน ISO 14882, 2nd Edition: Section 23.2.4 [lib.vector]: "องค์ประกอบของเวกเตอร์จะถูกจัดเก็บอย่างต่อเนื่องกันหมายความว่าถ้า v เป็นเวกเตอร์ <T, Allocator> โดยที่ T คือบางประเภทที่ไม่ใช่ บูลแล้วมันจะเป็นไปตามเอกลักษณ์ & v [n] == & v [0] + n สำหรับ 0 ทั้งหมด <= n <v.size () "
Mike Caron

4
ดังนั้น, TR, TC, :) จริงๆแล้ว C ++ 03 เรียกอีกอย่างว่า C ++ 98-TC1 (คอร์ริเจนดุมทางเทคนิค) จากสิ่งที่ฉันอ่าน
Johannes Schaub - litb

2
แล้วเวกเตอร์ของเวกเตอร์ล่ะ? เวกเตอร์เอ็นเนอร์อยู่หลังเวกเตอร์ภายในของกลุ่มสุดท้ายใช่หรือไม่
huseyin tugrul buyukisik

1
@huseyin tugrul buyukisik คุณได้เรียนรู้คำตอบของสิ่งนี้หรือไม่? ฉันยังสงสัยว่ามันทำงานอย่างไร
David Doria

1
@huseyin tugrul buyukisik แน่นอนว่ามันเป็นเรื่องจริง แต่มันเป็นกรณีของเหตุการณ์ต่อมาstd::vectorที่ต่อเนื่องกัน เช่น: ในstd::vector<std::vector<int>> vองค์ประกอบv[0], v[1]... จะถูกเก็บไว้ในหน่วยความจำในภายหลัง แต่องค์ประกอบv[0].back()และv[1].front()ไม่รับประกันว่าจะเป็น
jarzec

27

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

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


1
องค์ประกอบต่างๆจะยังคงถูกเก็บไว้ในบล็อกหน่วยความจำที่ต่อเนื่องกันมันจะอยู่ในที่อื่น คำถามเกี่ยวกับความต่อเนื่องกันโดยเฉพาะ
Dima

2
แต่ตัวชี้และตัวทำซ้ำที่มีอยู่จะไม่ถูกต้อง
Bill Lynch

จุดดี. คุณควรใส่ไว้ในคำตอบของคุณเพื่อชี้แจงว่าคุณหมายถึงอะไร
Dima

คำตอบที่เป็นประโยชน์ที่สุดสำหรับฉัน
CoffeDeveloper

ตอนนี้ฉันรู้แล้วว่าทำไมโปรแกรมของฉันถึงผิดพลาดเมื่อวานนี้เมื่อฉันวนซ้ำในการวนซ้ำสองครั้งเพื่อลบองค์ประกอบบางอย่างออก :) ขอบคุณ!
user2891462

9

ในความเป็นจริงมาตรฐานรับประกันว่า a vectorมีความต่อเนื่องในหน่วยความจำและ&a[0]สามารถส่งผ่านไปยังCฟังก์ชันที่ต้องการอาร์เรย์ได้

ข้อยกเว้นของกฎนี้คือvector<bool>ใช้เพียงหนึ่งบิตต่อboolดังนั้นแม้ว่ามันจะมีหน่วยความจำต่อเนื่อง แต่ก็ไม่สามารถใช้เป็น a ได้bool*(ถือว่าเป็นการเพิ่มประสิทธิภาพที่ผิดพลาดและผิดพลาด)

BTW ทำไมคุณไม่ใช้ตัวทำซ้ำ นั่นคือสิ่งที่พวกเขามีไว้สำหรับ


1
> BTW ทำไมคุณไม่ใช้ตัววนซ้ำ? นั่นคือสิ่งที่พวกเขามีไว้สำหรับ บางทีเขาอาจอ่านบทความใหม่ของ Alexanrescu ในหัวข้อ: boostcon.com/site-media/var/sphene/sphwiki/attachment/2009/05/…
Nemanja Trifunovic

ขอบคุณสำหรับลิงค์ฉันจะไปที่รายการเรื่องรออ่านของฉัน (ฉันพยายามอย่าพลาดบทความของ Alexandresu)
Motti

Mwahaha ทุกคนดูเหมือนจะพูดถึงการนำเสนอในวันนี้ ดูการสนทนายังคงร้อนแรงอยู่: groups.google.com/group/comp.lang.c++.moderated/browse_thread/…
Johannes Schaub - litb

หากคุณอ่านอย่างละเอียดบทความของ Alexandrescu ไม่ได้บอกว่า "อย่าใช้ iterators ใน C ++" แต่ก็มีข้อความว่า "Check out D" วิธีการที่เขาอธิบายในเอกสารนั้นมีความคล้ายคลึงอย่างมากกับภาษาและกรอบการทำงานที่มีอยู่ซึ่งได้ดูดซับมรดกการใช้งาน (รายการ, โครงการ, Haskell) และฉันสงสัยอย่างจริงจังว่าไวยากรณ์ที่ใช้ C เป็นอีกจุดเริ่มต้นที่ดีหรือไม่ การจัดการรายการ บางครั้งเมื่อปีที่แล้วฉันพยายามเกลี้ยกล่อมให้เขาเปลี่ยนความสามารถจำนวนมากไปสู่การพัฒนาภาษาที่เป็นที่ยอมรับแล้วเช่น C # แทน แต่ฉันกลัวว่าจะไม่ประสบความสำเร็จ! :)
Daniel Earwicker

6

ดังที่คนอื่น ๆ ได้กล่าวไว้แล้วว่าvectorภายในใช้อาร์เรย์ของวัตถุที่อยู่ติดกัน ตัวชี้ในอาร์เรย์นั้นควรถือว่าไม่ถูกต้องเมื่อใดก็ตามที่ฟังก์ชันสมาชิกที่ไม่ใช่ const เรียกว่า IIRC

อย่างไรก็ตามมีข้อยกเว้น !!

vector<bool>มีการใช้งานเฉพาะที่ออกแบบมาเพื่อประหยัดพื้นที่ดังนั้นแต่ละบูลจึงใช้เพียงบิตเดียว อาร์เรย์พื้นฐานไม่ได้เป็นอาร์เรย์ที่ต่อเนื่องกันของบูลและการคำนวณบนอาร์เรย์vector<bool>ไม่ทำงานเช่นvector<T>หากว่า

(ฉันคิดว่าอาจเป็นไปได้ว่านี่อาจเป็นจริงสำหรับความเชี่ยวชาญพิเศษของเวกเตอร์เนื่องจากเราสามารถใช้งานใหม่ได้ตลอดเวลาอย่างไรก็ตามstd::vector<bool>เป็นเพียงความเชี่ยวชาญมาตรฐานเดียวที่ผิดพลาดมาตรฐานซึ่งการคำนวณทางคณิตศาสตร์ของตัวชี้อย่างง่ายจะใช้ไม่ได้)


ผู้ใช้ไม่ได้รับอนุญาตให้เชี่ยวชาญstd::vectorและเวกเตอร์อื่น ๆ ทั้งหมดจำเป็นต้องใช้พื้นที่จัดเก็บที่ต่อเนื่องกัน ดังนั้นstd::vector<bool>(โชคดี) เวกเตอร์มาตรฐานเดียวที่แปลก (ฉันมีความเห็นอย่างยิ่งว่าความเชี่ยวชาญนี้ควรได้รับการเลิกใช้และแทนที่ด้วยเช่น a ที่std::dynamic_bitsetมีฟังก์ชันการทำงานเหมือนกันมากไม่ใช่โครงสร้างข้อมูลที่ไม่ดีไม่ใช่แค่เวกเตอร์)
Arne Vogel

3

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

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

void generate(std::vector<float> v)
{
  float f = generate_next_float();
  v.push_back(f);
}

ตอนนี้ฉันสามารถส่งการลอยของเวกเตอร์เป็นอาร์เรย์ไปยังฟังก์ชันที่เกี่ยวข้องกับบัฟเฟอร์ของ OpenGL ได้ นอกจากนี้ยังขจัดความจำเป็นในการกำหนดขนาดของอาร์เรย์เพื่อกำหนดความยาวของอาร์เรย์

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


2
ฟังก์ชั่นนี้ไม่สมเหตุสมผลกับฉันเลย คุณหมายถึงการส่งต่อข้อมูลอ้างอิงหรือตัวชี้vมากกว่าvตัวมันเอง? เนื่องจากการผ่านvเพียงอย่างเดียวจะทำให้สำเนาถูกสร้างขึ้นภายในฟังก์ชันซึ่งจะไม่มีอยู่หลังจากฟังก์ชันสิ้นสุดลง ดังนั้นคุณจะผลักบางสิ่งลงบนเวกเตอร์เท่านั้นเพื่อลบเวกเตอร์เมื่อฟังก์ชันสิ้นสุดลง
johnbakers

1

cplusplus.com:

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


1

ใช่องค์ประกอบของ std :: vector รับประกันได้ว่าติดกัน


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