Pourquoi ne pouvons-nous pas changer le pointeur de données de std :: vector?
J'ai un tableau char* source
et un vecteur std::vector<char> target
. Je voudrais faire target
pointer le vecteur source
dans O (1), sans copier les données.
Quelque chose dans ce sens:
#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;
Pourquoi n'est-il pas possible de modifier manuellement l'emplacement vers lequel pointe un vecteur? Y a-t-il un problème spécifique et ingérable qui se poserait si cela était possible?
Réponses
La vector
classe C ++ prend en charge l'ajout et la suppression d'éléments, avec un ordre consécutif garanti en mémoire. Si vous pouviez initialiser votre vector
avec la mémoire tampon existante et y ajouter suffisamment d'éléments, cela déborderait ou nécessiterait une réallocation.
L'interface de vector
suppose qu'elle gère sa mémoire tampon interne, c'est-à-dire qu'elle peut l'allouer, la désallouer, la redimensionner quand elle le souhaite (dans les spécifications, bien sûr). Si vous avez besoin de quelque chose qui n'est pas autorisé à gérer sa mémoire tampon, vous ne pouvez pas utiliser vector
- utilisez une structure de données différente ou écrivez-en une vous-même.
Vous pouvez créer un vector
objet en copiant vos données (en utilisant un constructeur avec deux pointeurs ou assign
), mais ce n'est évidemment pas ce que vous voulez.
Alternativement, vous pouvez utiliser string_view, qui ressemble presque ou peut-être exactement ce dont vous avez besoin.
std::vector
est considéré comme le propriétaire du tampon sous-jacent. Vous pouvez changer le tampon mais ce changement provoque une allocation c'est-à-dire faire une copie du tampon source dont vous ne voulez pas (comme indiqué dans la question).
Vous pouvez faire ce qui suit:
#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;
}
mais encore une fois std::vector::assign:
Remplace le contenu par des copies de ceux de la plage [premier, dernier).
La copie est donc à nouveau exécutée. Vous ne pouvez pas vous en éloigner pendant l'utilisation std::vector
.
Si vous ne voulez pas copier de données, vous devez utiliser à std::span
partir de C ++ 20 (ou créer votre propre span) ou use std::string_view
(qui vous semble approprié puisque vous avez un tableau de char
s).
1ère option: Utilisation std::string_view
Puisque vous êtes limité à C ++ 17, cela std::string_view
pourrait être parfait pour vous. Il construit une vue des 3 premiers caractères du tableau de caractères en commençant par l'élément pointé par 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ème option: Utilisation à std::span
partir de C ++ 20
std::span
vient de C ++ 20 donc ce n'est peut-être pas le moyen le plus parfait pour vous, mais vous pourriez être intéressé par ce que c'est et comment cela fonctionne. Vous pouvez penser std::span
à une version un peu généralisée de std::string_view
car il s'agit d'une séquence contiguë d'objets de tout type, pas seulement de caractères. L'utilisation est similaire à celle du 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ème option: votre propre portée
Si vous êtes limité à C ++ 17, vous pouvez penser à créer votre propre span
structure. C'est peut-être encore exagéré, mais laissez-moi vous montrer (au fait, jetez un œil à cette réponse plus élaborée ):
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;
}
L'utilisation est donc la même que celle de la version C ++ 20 de std::span
.