Perché non possiamo cambiare il puntatore ai dati di std :: vector?
Ho un array char* source
e un vettore std::vector<char> target
. Vorrei fare in modo che il vettore target
punti source
in O (1), senza copiare i dati.
Qualcosa in questo senso:
#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;
Perché non è possibile modificare manualmente il punto a cui punta un vettore? C'è qualche problema specifico e ingestibile che sorgerebbe se ciò fosse possibile?
Risposte
La vector
classe C ++ supporta l'aggiunta e l'eliminazione di elementi, con un ordine consecutivo garantito in memoria. Se potessi inizializzare il tuo vector
con il buffer di memoria esistente e aggiungere abbastanza elementi ad esso, andrebbe in overflow o richiederebbe la riallocazione.
L'interfaccia di vector
assume che gestisce il suo buffer interno, cioè può allocarlo, deallocarlo, ridimensionarlo ogni volta che vuole (all'interno delle specifiche, ovviamente). Se hai bisogno di qualcosa che non è autorizzato a gestire il suo buffer, non puoi usare vector
: usa una struttura dati diversa o scrivine una tu stesso.
Puoi creare un vector
oggetto copiando i tuoi dati (usando un costruttore con due puntatori o assign
), ma questo ovviamente non è quello che vuoi.
In alternativa, puoi usare string_view, che sembra quasi o forse esattamente quello di cui hai bisogno.
std::vector
è considerato il proprietario della riserva sottostante. È possibile modificare il buffer ma questa modifica provoca l'allocazione, ovvero la creazione di una copia del buffer di origine che non si desidera (come indicato nella domanda).
Potresti fare quanto segue:
#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;
}
ma ancora std::vector::assign:
Sostituisce il contenuto con copie di quelli nell'intervallo [primo, ultimo).
Quindi la copia viene eseguita di nuovo. Non puoi allontanarti da esso durante l'utilizzo std::vector
.
Se non vuoi copiare i dati, dovresti usare std::span
da C ++ 20 (o creare il tuo span) o usare std::string_view
(che sembra adatto a te dato che hai un array di char
s).
1a opzione: utilizzo di std::string_view
Dato che sei limitato a C ++ 17, std::string_view
potrebbe essere perfetto per te. Costruisce una vista dei primi 3 caratteri della matrice di caratteri a partire dall'elemento puntato da 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;
}
Seconda opzione: utilizzo std::span
da C ++ 20
std::span
proviene da C ++ 20 quindi potrebbe non essere il modo più perfetto per te, ma potresti essere interessato a cosa è e come funziona. Puoi pensare std::span
a una versione un po 'generalizzata std::string_view
perché è una sequenza contigua di oggetti di qualsiasi tipo, non solo caratteri. L'utilizzo è simile a quello di 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;
}
3a opzione: il tuo intervallo
Se sei limitato a C ++ 17, puoi pensare di creare la tua span
struttura. Potrebbe essere ancora eccessivo, ma lascia che te lo mostri (dai un'occhiata a questa risposta più elaborata ):
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;
}
Quindi l'utilizzo è lo stesso della versione C ++ 20 di std::span
.