วิธีการเริ่มต้น std :: vector จากอาร์เรย์ C-style?


174

วิธีที่ถูกที่สุดในการเริ่มต้นstd::vectorอาร์เรย์จาก C-style คืออะไร?

ตัวอย่าง: ในคลาสต่อไปนี้ฉันมีvectorแต่เนื่องจากข้อ จำกัด ภายนอกข้อมูลจะถูกส่งผ่านเป็นอาร์เรย์สไตล์ C:

class Foo {
  std::vector<double> w_;
public:
  void set_data(double* w, int len){
   // how to cheaply initialize the std::vector?
}

เห็นได้ชัดว่าผมสามารถโทรและห่วงแล้วกว่าองค์ประกอบหรือโทรw_.resize() std::copy()มีวิธีการที่ดีกว่านี้ไหม?


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

คำตอบ:


233

อย่าลืมว่าคุณสามารถใช้พอยน์เตอร์เป็นตัววนซ้ำได้:

w_.assign(w, w + len);

3
มันเป็นปัญหาคุณภาพของการใช้งาน เนื่องจากตัววนซ้ำมีแท็กที่ระบุหมวดหมู่ของพวกเขาการใช้งานassignนั้นจึงไม่มีค่าใช้จ่ายเพื่อเพิ่มประสิทธิภาพ อย่างน้อยใน VC ++ มันทำอย่างนั้นจริง ๆ
Pavel Minaev

38
วิธีแก้ปัญหาอย่างรวดเร็วอาจเป็นมาตรฐาน :: vector <double> w_ (w, w + len);
jamk

1
สิ่งนี้จะคัดลอกองค์ประกอบไปยังที่เก็บข้อมูลที่สร้างขึ้นใหม่สำหรับ 'w_'; 'w_.data' จะไม่ชี้ไปที่ 'w' คุณยังคงต้องยกเลิกการจัดสรร 'w' ไม่มีการโอนกรรมสิทธิ์
Indy9000

1
หากองค์ประกอบหนึ่งผ่านจุดสิ้นสุดมันควรจะโอเค (เช่นเดียวกับv.end()ตัววนซ้ำที่ชี้ไปหนึ่งจุดสิ้นสุดด้วยเวกเตอร์ในกรณีที่คล้ายกัน) หากคุณได้รับการยืนยันแล้วบางสิ่งบางอย่างออกไปที่อื่น
Pavel Minaev

1
เร็ว ๆ นี้จะยกเลิกการจัดสรรหน่วยความจำอาร์เรย์เมื่อเวกเตอร์ออกนอกขอบเขตหรือไม่
Adrian Koch

41

คุณใช้คำว่าการเตรียมใช้งานดังนั้นจึงไม่ชัดเจนหากเป็นการมอบหมายแบบครั้งเดียวหรืออาจเกิดขึ้นหลายครั้ง

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

Foo::Foo(double* w, int len) : w_(w, w + len) { }

มิฉะนั้นให้ใช้การมอบหมายตามที่แนะนำไว้ก่อนหน้านี้:

void set_data(double* w, int len)
{
    w_.assign(w, w + len);
}

1
ในกรณีของฉันการบ้านจะเกิดขึ้นซ้ำ ๆ
แฟรงค์

12

คุณสามารถ 'เรียนรู้' ขนาดของอาร์เรย์โดยอัตโนมัติ:

template<typename T, size_t N>
void set_data(const T (&w)[N]){
    w_.assign(w, w+N);
}

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


มันทำงานอย่างไร

[อัปเดต: ดูที่นี่สำหรับการอภิปรายที่ครอบคลุมมากขึ้นเกี่ยวกับการเรียนรู้ขนาด]

นี่เป็นวิธีแก้ปัญหาทั่วไปเพิ่มเติม:

template<typename T, size_t N>
void copy_from_array(vector<T> &target_vector, const T (&source_array)[N]) {
    target_vector.assign(source_array, source_array+N);
}

วิธีนี้ใช้งานได้เนื่องจากอาเรย์ถูกส่งผ่านเป็นการอ้างอิงไปยังอาเรย์ ใน C / C ++ คุณไม่สามารถผ่านอาร์เรย์เป็นฟังก์ชั่นแทนมันจะสลายตัวไปยังตัวชี้และคุณสูญเสียขนาด แต่ใน C ++ คุณสามารถส่งการอ้างอิงไปยังอาร์เรย์ได้

ผ่านอาร์เรย์โดยอ้างอิงต้องใช้ประเภทให้ตรงทั้งหมด ขนาดของอาร์เรย์เป็นส่วนหนึ่งของชนิด ซึ่งหมายความว่าเราสามารถใช้พารามิเตอร์เทมเพลต N เพื่อเรียนรู้ขนาดสำหรับเรา

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

template<typename T, size_t N>
vector<T> convert_array_to_vector(const T (&source_array)[N]) {
    return vector<T>(source_array, source_array+N);
}

1
ในตัวอย่างล่าสุดreturn { begin(source_array), end(source_array) };ยังเป็นไปได้
MM

12

พาเวลอยู่ใกล้ แต่ก็มีวิธีที่ง่ายและสง่างามกว่าในการเริ่มต้นคอนเทนเนอร์ตามลำดับจากอาร์เรย์สไตล์ ac

ในกรณีของคุณ:

w_ (array, std::end(array))
  • อาร์เรย์จะทำให้เราได้ตัวชี้ไปยังจุดเริ่มต้นของอาร์เรย์ (ไม่พบชื่อมัน)
  • std :: end (array) จะทำให้เราเป็นตัววนซ้ำไปยังจุดสิ้นสุดของอาร์เรย์

1
สิ่งนี้รวม / รุ่น C ++ ต้องการอะไรบ้าง?
Vlad

1
นี่คือหนึ่งในตัวสร้างของ std :: vector จากอย่างน้อย c ++ 98 เป็นต้นไป ... เรียกว่า 'range constructor' cplusplus.com/reference/vector/vector/vectorลองดู
Mugurel

1
เวอร์ชันที่เป็นอิสระมากขึ้นคือ: w_ (std :: start (array), std :: end (array)); (ในอนาคตคุณสามารถเปลี่ยนอาร์เรย์ C สำหรับคอนเทนเนอร์ C ++ ได้)
Andrew Romanov

13
โปรดทราบว่าจะใช้งานได้เฉพาะเมื่อคุณมีarrayอาร์เรย์จริง(ซึ่งโดยปกติหมายถึงคุณกำลังคัดลอกจากอาร์เรย์ทั่วโลกหรือท้องถิ่น (ประกาศในฟังก์ชันปัจจุบัน)) ในกรณีของ OP เขาได้รับพอยน์เตอร์และความยาวและเนื่องจากมันไม่ได้ templated ตามความยาวพวกเขาไม่สามารถเปลี่ยนเป็นการรับพอยน์เตอร์ไปยังอาร์เรย์ที่มีขนาดหรืออะไรก็ได้ดังนั้นstd::endจะไม่ทำงาน
ShadowRanger

2
vectorไม่เกินพิกัดoperator()ดังนั้นสิ่งนี้จะไม่รวบรวม std::endการเรียกใช้พอยน์เตอร์นั้นไม่มีประโยชน์ใด ๆ (คำถามที่ถามให้กำหนดเวกเตอร์จากตัวชี้และตัวแปรความยาวแยก) มันจะปรับปรุงคำตอบของคุณเพื่อแสดงบริบทเพิ่มเติมเกี่ยวกับสิ่งที่คุณพยายามแนะนำ
MM

2

คำตอบทั่วไปอย่างรวดเร็ว:

std::vector<double> vec(carray,carray+carray_size); 

หรือคำถามเฉพาะ:

std::vector<double> w_(w,w+len); 

ตามข้างต้น : อย่าลืมว่าคุณสามารถใช้พอยน์เตอร์เป็นตัววนซ้ำได้


0

std::vector<double>::assignเป็นวิธีที่จะไปเพราะมันเป็นรหัสเล็ก ๆ น้อย ๆ แต่มันใช้งานได้จริงอย่างไร มันไม่ได้ปรับขนาดแล้วคัดลอก? ในการใช้ MS ของ STL ฉันใช้มันทำอย่างนั้น

ฉันกลัวมีไม่ได้เร็วขึ้นวิธีที่จะใช้ (อีกครั้ง) std::vectorการเริ่มต้นของคุณ


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

นั่นคือคำตอบหรือคำถาม? อะไรนำมาสู่คำตอบที่มีอยู่แล้ว?
Jean-François Fabre

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