Por que não podemos alterar o ponteiro de dados de std :: vector?
Eu tenho uma matriz char* source
e um vetor std::vector<char> target
. Eu gostaria de fazer o vetor target
apontar para source
em O (1), sem copiar os dados.
Algo nesse sentido:
#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 que não é possível alterar manualmente para onde um vetor aponta? Existe algum problema específico e incontrolável que surgiria se isso fosse possível?
Respostas
A vector
classe C ++ oferece suporte à adição e exclusão de elementos, com ordem consecutiva garantida na memória. Se você pudesse inicializar seu vector
com o buffer de memória existente e adicionar elementos suficientes a ele, ele estouraria ou exigiria realocação.
A interface do vector
assume que gerencia seu buffer interno, ou seja, pode alocar, desalocar, redimensionar quando quiser (dentro das especificações, é claro). Se você precisa de algo que não tem permissão para gerenciar seu buffer, você não pode usar vector
- use uma estrutura de dados diferente ou escreva uma você mesmo.
Você pode criar um vector
objeto copiando seus dados (usando um construtor com dois ponteiros ou assign
), mas obviamente não é isso que você deseja.
Como alternativa, você pode usar o string_view, que parece quase ou talvez exatamente o que você precisa.
std::vector
é considerado o proprietário do buffer subjacente. Você pode alterar o buffer, mas essa alteração causa alocação, ou seja, fazer uma cópia do buffer de origem que você não deseja (conforme declarado na pergunta).
Você pode fazer o seguinte:
#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;
}
mas novamente std::vector::assign:
Substitui o conteúdo por cópias daqueles no intervalo [primeiro, último).
Portanto, a cópia é executada novamente. Você não pode fugir dele durante o uso std::vector
.
Se não quiser copiar dados, você deve usar std::span
do C ++ 20 (ou criar seu próprio intervalo) ou usar std::string_view
(que parece adequado para você, pois você tem uma matriz de char
s).
1ª opção: usando std::string_view
Como você está limitado a C ++ 17, std::string_view
pode ser perfeito para você. Ele constrói uma visão dos primeiros 3 caracteres da matriz de caracteres, começando com o elemento apontado 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;
}
2ª opção: usando a std::span
partir de C ++ 20
std::span
vem do C ++ 20, então pode não ser a maneira mais perfeita para você, mas você pode estar interessado em saber o que é e como funciona. Você pode pensar nisso std::span
como uma versão um pouco generalizada de std::string_view
porque é uma sequência contígua de objetos de qualquer tipo, não apenas caracteres. O uso é semelhante ao 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;
}
3ª opção: seu próprio período
Se você está limitado a C ++ 17, pode pensar em criar sua própria span
estrutura. Ainda pode ser um exagero, mas deixe-me mostrar a você (a propósito, dê uma olhada nesta resposta mais 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;
}
Portanto, o uso é o mesmo da versão C ++ 20 do std::span
.