Mengapa kita tidak bisa mengubah penunjuk data dari std :: vector?
Saya memiliki array char* source
dan vektor std::vector<char> target
. Saya ingin membuat target
titik vektor ke source
dalam O (1), tanpa menyalin data.
Sesuatu seperti ini:
#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;
Mengapa tidak mungkin untuk mengubah secara manual di mana vektor menunjuk? Adakah masalah khusus yang tidak dapat ditangani yang akan muncul jika hal ini memungkinkan?
Jawaban
vector
Kelas C ++ mendukung penambahan dan penghapusan elemen, dengan urutan yang dijamin dalam memori. Jika Anda dapat menginisialisasi Anda vector
dengan buffer memori yang ada, dan menambahkan elemen yang cukup ke dalamnya, itu akan meluap atau memerlukan realokasi.
Antarmuka vector
mengasumsikan bahwa ia mengelola buffer internalnya, yaitu, ia dapat mengalokasikan, membatalkan alokasi, mengubah ukurannya kapan pun ia mau (dalam spesifikasi, tentu saja). Jika Anda memerlukan sesuatu yang tidak diizinkan untuk mengelola buffernya, Anda tidak dapat menggunakan vector
- gunakan struktur data yang berbeda atau tulis sendiri.
Anda dapat membuat vector
objek dengan menyalin data Anda (menggunakan konstruktor dengan dua penunjuk atau assign
), tetapi ini jelas bukan yang Anda inginkan.
Atau, Anda dapat menggunakan string_view, yang terlihat hampir atau mungkin persis seperti yang Anda butuhkan.
std::vector
dianggap sebagai pemilik buffer yang mendasarinya. Anda dapat mengubah buffer tetapi perubahan ini menyebabkan alokasi, yaitu membuat salinan buffer sumber yang tidak Anda inginkan (seperti yang dinyatakan dalam pertanyaan).
Anda dapat melakukan hal berikut:
#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;
}
tapi sekali lagi std::vector::assign:
Mengganti konten dengan salinan yang ada dalam rentang [pertama, terakhir).
Jadi penyalinan dilakukan lagi. Anda tidak bisa lepas darinya saat menggunakan std::vector
.
Jika Anda tidak ingin menyalin data, maka Anda harus menggunakan std::span
dari C ++ 20 (atau buat span Anda sendiri) atau gunakan std::string_view
(yang terlihat cocok untuk Anda karena Anda memiliki array char
s).
Opsi pertama: Menggunakan std::string_view
Karena Anda terbatas pada C ++ 17, std::string_view
mungkin cocok untuk Anda. Ini membangun tampilan dari 3 karakter pertama dari larik karakter yang dimulai dengan elemen yang ditunjuk oleh 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;
}
Opsi kedua: Menggunakan std::span
dari C ++ 20
std::span
berasal dari C ++ 20 jadi ini mungkin bukan cara yang paling sempurna untuk Anda, tetapi Anda mungkin tertarik dengan apa itu dan bagaimana cara kerjanya. Anda dapat menganggapnya std::span
sebagai versi yang sedikit digeneralisasi std::string_view
karena ini adalah urutan objek yang berdekatan dari jenis apa pun, bukan hanya karakter. Penggunaannya mirip dengan 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;
}
Opsi ketiga: Rentang Anda sendiri
Jika Anda terbatas pada C ++ 17, Anda dapat memikirkan untuk membuat span
struct Anda sendiri . Ini mungkin masih berlebihan tapi izinkan saya menunjukkan kepada Anda (btw lihat jawaban yang lebih terperinci ini ):
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;
}
Jadi penggunaannya sama dengan versi C ++ 20 std::span
.