¿Por qué no podemos cambiar el puntero de datos de std :: vector?
Tengo una matriz char* source
y un vector std::vector<char> target
. Me gustaría hacer que el vector target
apunte source
en O (1), sin copiar los datos.
Algo en esta línea:
#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;
¿Por qué no es posible cambiar manualmente a dónde apunta un vector? ¿Existe algún problema específico inmanejable que surgiría si esto fuera posible?
Respuestas
La vector
clase C ++ admite la adición y eliminación de elementos, con un orden consecutivo garantizado en la memoria. Si pudiera inicializar su vector
con el búfer de memoria existente y agregarle suficientes elementos, se desbordaría o requeriría reasignación.
La interfaz de vector
asume que administra su búfer interno, es decir, puede asignarlo, desasignarlo, redimensionarlo cuando quiera (dentro de las especificaciones, por supuesto). Si necesita algo que no esté autorizado para administrar su búfer, no puede usarlo vector
: use una estructura de datos diferente o escriba una usted mismo.
Puede crear un vector
objeto copiando sus datos (usando un constructor con dos punteros o assign
), pero obviamente esto no es lo que desea.
Alternativamente, puede usar string_view, que se ve casi o tal vez exactamente lo que necesita.
std::vector
se considera el propietario del búfer subyacente. Puede cambiar el búfer, pero este cambio provoca la asignación, es decir, hacer una copia del búfer de origen que no desea (como se indica en la pregunta).
Podrías hacer lo siguiente:
#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;
}
pero de nuevo std::vector::assign:
Reemplaza el contenido con copias de aquellos en el rango [primero, último).
Entonces se vuelve a realizar la copia. No puedes alejarte de él mientras lo usas std::vector
.
Si no desea copiar datos, debe usar std::span
desde C ++ 20 (o crear su propio intervalo) o usar std::string_view
(que parece adecuado para usted ya que tiene una matriz de char
s).
1ra opción: Usando std::string_view
Dado que está limitado a C ++ 17, std::string_view
podría ser perfecto para usted. Construye una vista de los primeros 3 caracteres de la matriz de caracteres comenzando con el elemento señalado por 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;
}
2da opción: Usando std::span
desde C ++ 20
std::span
proviene de C ++ 20, por lo que puede que no sea la forma más perfecta para usted, pero es posible que le interese qué es y cómo funciona. Puede pensar std::span
en una versión un poco generalizada de std::string_view
porque es una secuencia contigua de objetos de cualquier tipo, no solo caracteres. El uso es similar al de 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;
}
Tercera opción: tu propio tramo
Si está limitado a C ++ 17, puede pensar en crear su propia span
estructura. Todavía puede ser una exageración, pero déjeme mostrarle (por cierto, eche un vistazo a esta respuesta más elaborada ):
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;
}
Entonces, el uso es el mismo que con la versión de C ++ 20 de std::span
.