Warum können wir den Datenzeiger von std :: vector nicht ändern?
Ich habe ein Array char* source
und einen Vektor std::vector<char> target
. Ich mag den Vektor machen target
Punkt source
in O (1), ohne die Daten zu kopieren.
Etwas in diese Richtung:
#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;
Warum kann nicht manuell geändert werden, wohin ein Vektor zeigt? Gibt es ein spezifisches, unüberschaubares Problem, das auftreten würde, wenn dies möglich wäre?
Antworten
Die C ++ - vector
Klasse unterstützt das Hinzufügen und Löschen von Elementen mit garantierter fortlaufender Reihenfolge im Speicher. Wenn Sie Ihren vector
mit vorhandenem Speicherpuffer initialisieren und genügend Elemente hinzufügen könnten , würde er entweder überlaufen oder eine Neuzuweisung erfordern.
Die Schnittstelle von vector
geht davon aus, dass sie ihren internen Puffer verwaltet , dh sie kann ihn jederzeit zuweisen, freigeben und seine Größe ändern (natürlich innerhalb der Spezifikation). Wenn Sie brauchen etwas , das nicht erlaubt ist , zu verwalten sein Puffer, können Sie nicht verwenden vector
- verwenden Sie eine andere Datenstruktur oder Schreib selbst eine Beurteilung.
Sie können ein vector
Objekt erstellen, indem Sie Ihre Daten kopieren (mit einem Konstruktor mit zwei Zeigern oder assign
), aber dies ist offensichtlich nicht das, was Sie wollen.
Alternativ können Sie verwenden string_view, was fast oder vielleicht genau das aussieht, was Sie brauchen.
std::vector
wird als Eigentümer des zugrunde liegenden Puffers angesehen. Sie können den Puffer ändern, aber diese Änderung bewirkt eine Zuordnung, dh das Erstellen einer Kopie des Quellpuffers, die Sie nicht möchten (wie in der Frage angegeben).
Sie können Folgendes tun:
#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;
}
aber nochmal std::vector::assign:
Ersetzt den Inhalt durch Kopien der Inhalte im Bereich [first, last].
Der Kopiervorgang wird also erneut durchgeführt. Sie können während der Verwendung nicht davon loskommen std::vector
.
Wenn Sie keine Daten kopieren möchten, sollten Sie std::span
C ++ 20 verwenden (oder eine eigene Spanne erstellen) oder verwenden std::string_view
(was für Sie geeignet erscheint, da Sie ein Array von char
s haben).
1. Option: Verwenden std::string_view
Da Sie auf C ++ 17 beschränkt sind, ist dies std::string_view
möglicherweise perfekt für Sie. Es erstellt eine Ansicht der ersten 3 Zeichen des Zeichenarrays, beginnend mit dem Element, auf das gezeigt wird 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. Option: Verwenden std::span
von C ++ 20
std::span
kommt aus C ++ 20, daher ist es möglicherweise nicht der perfekteste Weg für Sie, aber Sie könnten daran interessiert sein, was es ist und wie es funktioniert. Sie können sich std::span
eine etwas verallgemeinerte Version von std::string_view
vorstellen, da es sich um eine zusammenhängende Folge von Objekten jeglichen Typs handelt, nicht nur von Zeichen. Die Verwendung ist ähnlich wie bei 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. Option: Ihre eigene Spanne
Wenn Sie auf C ++ 17 beschränkt sind, können Sie eine eigene span
Struktur erstellen . Es könnte immer noch ein Overkill sein, aber ich möchte es Ihnen zeigen (sehen Sie sich übrigens diese ausführlichere Antwort an ):
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;
}
Die Verwendung ist also dieselbe wie bei der C ++ 20-Version von std::span
.