วิธีที่รวดเร็วในการคัดลอกเวกเตอร์หนึ่งไปยังอีก


155

ฉันชอบสองวิธี:

void copyVecFast(const vec<int>& original)
{
  vector<int> newVec;
  newVec.reserve(original.size());
  copy(original.begin(),original.end(),back_inserter(newVec));
}

void copyVecFast(vec<int>& original)
{

  vector<int> newVec;
  newVec.swap(original); 
}

คุณจะทำอย่างไรมันได้หรือไม่?


14
ชื่อที่สองมีชื่อที่ทำให้เข้าใจผิด - เนื่องจากไม่ใช่สำเนา (แม้ว่าจะรวดเร็ว)
ไม่ระบุชื่อ

คำตอบ:


126

ตัวอย่างที่สองของคุณไม่ทำงานหากคุณส่งการโต้แย้งโดยการอ้างอิง คุณหมายถึง

void copyVecFast(vec<int> original) // no reference
{

  vector<int> new_;
  new_.swap(original); 
}

นั่นจะใช้ได้ แต่วิธีที่ง่ายกว่าคือ

vector<int> new_(original);

ดีมันใช้งานได้ดี แต่มันไม่ทำงานสำหรับอาร์เรย์เวกเตอร์: เช่น: vector <int> A [n];
ABcDexter

8
นั่นคือการสลับไม่ใช่การคัดลอก
sdd

1
@sdd - ไม่มันไม่ใช่ ตรวจสอบรายการอาร์กิวเมนต์ originalเป็นสำเนาของอาร์กิวเมนต์ของฟังก์ชัน
rlbond

249

พวกเขาไม่เหมือนกันใช่มั้ย หนึ่งคือการคัดลอกส่วนหนึ่งคือการแลกเปลี่ยนคือการแลกเปลี่ยน ดังนั้นชื่อฟังก์ชั่น

สิ่งที่ฉันชอบคือ:

a = b;

ที่ไหนaและbมีเวกเตอร์


3
ในความเป็นจริงวิธีการที่จะผ่านไปตามค่าคอมไพเลอร์เรียกตัวสร้างสำเนาและจากนั้นการแลกเปลี่ยนองค์ประกอบที่สร้างขึ้นใหม่ นั่นคือเหตุผลที่ rlbond แนะนำให้เรียกตัวสร้างการคัดลอกโดยตรงเพื่อให้ได้ผลเหมือนกัน
David Rodríguez - dribeas

1
อย่างไรก็ตามคุณไม่สามารถเรียก rlbon ได้หากไม่มีฟังก์ชั่นที่ส่งค่าต้นฉบับเป็น val มิเช่นนั้นต้นฉบับจะถูกปล่อยว่างเปล่า วิธีที่สองทำให้แน่ใจได้ว่าคุณจะโทรตามมูลค่าเสมอและด้วยเหตุนี้คุณจะไม่สูญเสียวันที่ในเวกเตอร์ดั้งเดิม (สมมติว่าข้อเสนอ swap กับพอยน์เตอร์)
Eyad Ebrahim

นั่นจะไม่ย้ายองค์ประกอบของ b ไปยัง a (เหลือ b ด้วยขนาด == 0) หรือไม่
โจนาธาน

1
@ Jonathan สมมติว่าคุณกำลังพูดถึงa = bแล้วไม่มี การกำหนดวิธีการ: ตรวจaเท่ากับได้โดยไม่ต้องเปลี่ยนb bในทางตรงกันข้ามstd::swap(a, b)จะแลกเปลี่ยนเนื้อหาของพวกเขา (เพื่อb's sizeในขณะนี้จะเป็นสิ่งที่a' s เคยเป็นมาก่อน) คุณอาจกำลังคิดถึงการดำเนินการย้าย (เช่นเกิดขึ้นใน C ++ 11 แต่ไม่ใช่ในการมอบหมายปกติแบบนี้) การเคลื่อนไหวดังกล่าวจะออกไปbในรัฐอะแฮ่ม "น่าสนใจ" - ดูstackoverflow.com/questions/17730689/…
Daniel Earwicker

1
@ Jonathan &&หมายเหตุเครื่องหมายคู่ เวอร์ชันนั้นจะใช้สำหรับการอ้างอิงค่า rvalue เท่านั้น มันจะไม่ตรงกับค่าที่ไม่ใช่ const ใด ๆ (เช่นbในตัวอย่างของฉันด้านบน) คุณสามารถเปลี่ยนbให้เป็นหนึ่งเดียวได้ด้วยการพูดa = std::move(b);ดูที่en.cppreference.com/w/cpp/language/value_categoryเพื่อความซับซ้อนยิ่งขึ้น
Daniel Earwicker

74

นี่เป็นอีกวิธีที่ถูกต้องในการทำสำเนาของเวกเตอร์เพียงใช้ตัวสร้าง:

std::vector<int> newvector(oldvector);

นี่ง่ายกว่าการใช้std::copyเพื่อเดินเวกเตอร์ทั้งหมดตั้งแต่ต้นจนจบstd::back_insertลงในเวกเตอร์ใหม่

ที่ถูกกล่าวว่า.swap()คนของคุณไม่ได้คัดลอก แต่มันแลกเปลี่ยนสองเวกเตอร์ คุณจะปรับเปลี่ยนต้นฉบับให้ไม่มีอะไรอีกต่อไป ซึ่งไม่ได้คัดลอก


19

คำตอบโดยตรง:

  • ใช้=โอเปอเรเตอร์

เราสามารถใช้ฟังก์ชั่นสมาชิกสาธารณะstd::vector::operator=ของภาชนะstd::vectorสำหรับการกำหนดค่าจากเวกเตอร์ไปยังอีก

  • ใช้ฟังก์ชันตัวสร้าง

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

ข้อควรระวัง:

  • ไม่ได้ใช้ std::vector::swap

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

  • คัดลอกลึกหรือตื้น?

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

ตามวิกิพีเดีย:

สำเนาลึกหมายถึงฟิลด์ถูกยกเลิกการอ้างอิง: แทนที่การอ้างอิงไปยังวัตถุที่ถูกคัดลอกวัตถุคัดลอกใหม่จะถูกสร้างขึ้นสำหรับวัตถุที่อ้างอิงใด ๆ และการอ้างอิงถึงสิ่งเหล่านี้ที่อยู่ใน B

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

อ้างอิง

หน้าของstd::vectorบน cplusplus.com


14

คุณไม่ควรใช้ swap เพื่อคัดลอกเวกเตอร์มันจะเปลี่ยนเวกเตอร์ "ต้นฉบับ"

ส่งต้นฉบับเป็นพารามิเตอร์ไปที่ใหม่แทน



-14

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

newVec.resize(oldVec.size());
memcpy(&newVec.at(0), &oldVec.at(0), oldVec.size());

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

ใช่คุณถูก. ฉันไม่เห็นสิ่งนั้น @FDinoff ถึงแม้จะใช้งานได้ต่ำกว่าหนึ่งรายการทำไมคุณถึงแนะนำไม่ให้ใช้ memcpy ดูเหมือนว่าจะเร็วกว่า newVec = oldVec มาก memcpy (& newVec.at (0), & oldVec.at (0), oldVec.size () * sizeof (int));
sgowd

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

ฉันไม่ได้วิจารณ์คุณ .. ทีมของฉันก็แนะนำเหมือนกันและฉันก็พยายามที่จะเข้าใจ
sgowd

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