Por que não podemos alterar o ponteiro de dados de std :: vector?
Eu tenho uma matriz char* sourcee um vetor std::vector<char> target. Eu gostaria de fazer o vetor targetapontar para sourceem 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 vectorclasse C ++ oferece suporte à adição e exclusão de elementos, com ordem consecutiva garantida na memória. Se você pudesse inicializar seu vectorcom o buffer de memória existente e adicionar elementos suficientes a ele, ele estouraria ou exigiria realocação.
A interface do vectorassume 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 vectorobjeto 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::spando C ++ 20 (ou criar seu próprio intervalo) ou usar std::string_view(que parece adequado para você, pois você tem uma matriz de chars).
1ª opção: usando std::string_view
Como você está limitado a C ++ 17, std::string_viewpode 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::spanpartir de C ++ 20
std::spanvem 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::spancomo uma versão um pouco generalizada de std::string_viewporque é 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 spanestrutura. 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.