ทำไมเราไม่สามารถเปลี่ยนตัวชี้ข้อมูลของ std :: vector ได้?
ฉันมีอาร์เรย์และเวกเตอร์char* source
std::vector<char> target
ฉันต้องการให้เวกเตอร์target
ชี้ไปที่source
ใน O (1) โดยไม่ต้องคัดลอกข้อมูล
บางสิ่งบางอย่างตามบรรทัดเหล่านี้:
#include <vector>
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.setData(source); // <- Doesn't exist
// OR
std::swap(target.data(), source); // <- swap() does not support char*
delete[] source;
เหตุใดจึงไม่สามารถเปลี่ยนจุดที่เวกเตอร์ชี้ไปด้วยตนเองได้ มีปัญหาที่เฉพาะเจาะจงและไม่สามารถจัดการได้ซึ่งจะเกิดขึ้นหากเป็นไปได้หรือไม่?
คำตอบ
คลาส C ++ vector
รองรับการเพิ่มและลบองค์ประกอบพร้อมรับประกันลำดับต่อเนื่องในหน่วยความจำ หากคุณสามารถเริ่มต้นของคุณvector
ด้วยบัฟเฟอร์หน่วยความจำที่มีอยู่และเพิ่มองค์ประกอบให้เพียงพอมันอาจล้นหรือต้องมีการจัดสรรใหม่
อินเทอร์เฟซvector
สมมติว่ามันจัดการบัฟเฟอร์ภายในนั่นคือสามารถจัดสรรยกเลิกการจัดสรรปรับขนาดได้ทุกเมื่อที่ต้องการ (แน่นอนว่าอยู่ในข้อกำหนด) หากคุณต้องการสิ่งที่ไม่ได้รับอนุญาตให้จัดการบัฟเฟอร์คุณจะไม่สามารถใช้ได้vector
- ใช้โครงสร้างข้อมูลอื่นหรือเขียนด้วยตัวเอง
คุณสามารถสร้างvector
ออบเจ็กต์โดยการคัดลอกข้อมูลของคุณ (โดยใช้ตัวสร้างที่มีพอยน์เตอร์สองตัวหรือassign
) แต่นี่ไม่ใช่สิ่งที่คุณต้องการอย่างชัดเจน
หรือคุณสามารถใช้string_viewซึ่งมีลักษณะเกือบหรืออาจจะตรงกับที่คุณต้องการ
std::vector
จะถือว่าเป็นเจ้าของบัฟเฟอร์ที่อยู่เบื้องหลัง คุณสามารถเปลี่ยนบัฟเฟอร์ได้ แต่การเปลี่ยนแปลงนี้ทำให้เกิดการจัดสรรเช่นการทำสำเนาของบัฟเฟอร์ต้นทางที่คุณไม่ต้องการ (ตามที่ระบุไว้ในคำถาม)
คุณสามารถทำสิ่งต่อไปนี้:
#include <vector>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.assign(source, source + 3);
delete[] source;
return 0;
}
แต่อีกครั้งstd::vector::assign:
แทนที่เนื้อหาด้วยสำเนาของเนื้อหาในช่วง [แรกสุดท้าย)
ดังนั้นการคัดลอกจะดำเนินการอีกครั้ง std::vector
คุณไม่สามารถได้รับจากมันในขณะที่ใช้
หากคุณไม่ต้องการคัดลอกข้อมูลคุณควรใช้std::span
จาก C ++ 20 (หรือสร้างช่วงของคุณเอง) หรือใช้std::string_view
(ซึ่งดูเหมาะกับคุณเนื่องจากคุณมีอาร์เรย์char
)
ตัวเลือกที่ 1: การใช้ std::string_view
เนื่องจากคุณ จำกัด ไว้ที่ C ++ 17 std::string_view
อาจเหมาะสำหรับคุณ มันสร้างมุมมองของ 3 source
ตัวอักษรแรกของอาร์เรย์ตัวละครที่เริ่มต้นด้วยองค์ประกอบที่ชี้โดย
#include <iostream>
#include <string_view>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::string_view strv( source, 3 );
delete[] source;
return 0;
}
ตัวเลือกที่ 2: ใช้std::span
จาก C ++ 20
std::span
มาจาก C ++ 20 ดังนั้นจึงอาจไม่ใช่วิธีที่สมบูรณ์แบบที่สุดสำหรับคุณ แต่คุณอาจสนใจว่ามันคืออะไรและทำงานอย่างไร คุณสามารถคิดว่าstd::span
เป็นเวอร์ชันทั่วไปเล็กน้อยstd::string_view
เนื่องจากเป็นลำดับของออบเจ็กต์ประเภทใด ๆ ที่ต่อเนื่องกันไม่ใช่แค่อักขระ การใช้งานจะคล้ายกับstd::string_view
:
#include <span>
#include <iostream>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::span s( source, 3 );
delete[] source;
return 0;
}
ตัวเลือกที่ 3: ช่วงของคุณเอง
หากคุณถูก จำกัด ไว้ที่ C ++ 17 คุณสามารถคิดสร้างโครงสร้างของคุณเองspan
ได้ มันอาจจะยังเกินความสามารถ แต่ให้ฉันแสดงให้คุณเห็น (btw ลองดูคำตอบที่ละเอียดกว่านี้):
template<typename T>
class span {
T* ptr_;
std::size_t len_;
public:
span(T* ptr, std::size_t len) noexcept
: ptr_{ptr}, len_{len}
{}
T& operator[](int i) noexcept {
return *ptr_[i];
}
T const& operator[](int i) const noexcept {
return *ptr_[i];
}
std::size_t size() const noexcept {
return len_;
}
T* begin() noexcept {
return ptr_;
}
T* end() noexcept {
return ptr_ + len_;
}
};
int main() {
char* source = new char[3] { 1, 2, 3 };
span s( source, 3 );
delete[] source;
return 0;
}
ดังนั้นการใช้งานจึงเหมือนกับรุ่น C ++ 20 ของstd::span
.