Warum können wir den Datenzeiger von std :: vector nicht ändern?

Dec 30 2020

Ich habe ein Array char* sourceund einen Vektor std::vector<char> target. Ich mag den Vektor machen targetPunkt sourcein 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

7 anatolyg Dec 30 2020 at 17:39

Die C ++ - vectorKlasse unterstützt das Hinzufügen und Löschen von Elementen mit garantierter fortlaufender Reihenfolge im Speicher. Wenn Sie Ihren vectormit vorhandenem Speicherpuffer initialisieren und genügend Elemente hinzufügen könnten , würde er entweder überlaufen oder eine Neuzuweisung erfordern.

Die Schnittstelle von vectorgeht 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 vectorObjekt 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.

3 NutCracker Dec 30 2020 at 19:23

std::vectorwird 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::spanC ++ 20 verwenden (oder eine eigene Spanne erstellen) oder verwenden std::string_view(was für Sie geeignet erscheint, da Sie ein Array von chars haben).

1. Option: Verwenden std::string_view

Da Sie auf C ++ 17 beschränkt sind, ist dies std::string_viewmö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::spanvon C ++ 20

std::spankommt 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::spaneine etwas verallgemeinerte Version von std::string_viewvorstellen, 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 spanStruktur 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.