Gebruik std::vector als zicht op ruw geheugen
Ik'gebruik een externe bibliotheek die me op een bepaald punt een ruwe pointer geeft naar een array van gehele getallen en een grootte.
Nu wil ik std::vector
gebruiken om deze waarden ter plaatse te benaderen en te wijzigen, in plaats van ze te benaderen met ruwe pointers.
Hier is een voorbeeld dat het punt uitlegt:
size_t size = 0;
int * data = get_data_from_library(size); // raw data from library {5,3,2,1,4}, size gets filled in
std::vector<int> v = ????; // pseudo vector to be used to access the raw data
std::sort(v.begin(), v.end()); // sort raw data in place
for (int i = 0; i < 5; i++)
{
std::cout << data[i] << "\n"; // display sorted raw data
}
Verwachte output:
1
2
3
4
5
De reden is dat ik algoritmes van <algorithm>
(sorteren, elementen verwisselen enz.) op die gegevens moet toepassen.
Aan de andere kant zou de grootte van die vector nooit veranderd worden, dus push_back
, erase
, insert
zijn niet nodig om op die vector te werken.
Ik zou een vector kunnen construeren op basis van de gegevens uit de bibliotheek, die vector kunnen wijzigen en de gegevens terug naar de bibliotheek kunnen kopiëren, maar dat zouden twee volledige kopieën zijn die ik zou willen vermijden omdat de dataset erg groot zou kunnen zijn.
C++20's
std::span
Als u C++20 kunt gebruiken, kunt u
std::span
gebruiken, wat een aanwijzer is - lengtepaar dat de gebruiker een beeld geeft van een aaneengesloten reeks elementen. Het is een soort vanstd::string_view
, en terwijl zowelstd::span
alsstd::string_view
niet-eigenarenaanzichten zijn, isstd::string_view
een alleen-lezen-aanzicht.Van de docs:
Dus het volgende zou werken:
Het probleem is dat
std::vector
een kopie moet maken van de elementen uit de array waarmee je het initialiseert, omdat het de eigendom heeft van de objecten die het bevat.Om dit te voorkomen, kunt u een slice object gebruiken voor een array (d.w.z., vergelijkbaar met wat
std::string_view
is voorstd::string
). Je zou je eigenarray_view
klasse sjabloonimplementatie kunnen schrijven waarvan de instanties worden geconstrueerd door een ruwe pointer naar het eerste element van een array en de lengte van de array te nemen:Array_view
slaat geen array op; het houdt alleen een pointer vast naar het begin van de array en de lengte van die array. Daarom zijn
array_view` objecten goedkoop om te construeren en te kopiëren.Aangezien
array_view
debegin()
eneinde()
lidfuncties biedt, kunt u de standaard bibliotheekalgoritmes (b.v.std::sorteren
,std::vinden
,std::lager_gebonden
, etc.) erop gebruiken:Aangezien de algoritme-bibliotheek met iterators werkt, kun je de array behouden.
Voor pointers en bekende array lengte
Hier kun je raw pointers als iterators gebruiken. Ze ondersteunen alle opertaties die een iterator ondersteunt (increment, vergelijking voor gelijkheid, waarde van, etc...):
Je kunt iterators krijgen op ruwe arrays en ze gebruiken in algoritmes:
Dat kan niet. Dat is niet waar
std::vector
voor is.std::vector
beheert zijn eigen buffer, die altijd wordt verkregen van een toewijzer. Hij neemt nooit een andere buffer in bezit (behalve van een andere vector van hetzelfde type).Aan de andere kant hoef je dat ook niet te doen, want ...
Die algoritmes werken op iteratoren. Een pointer is een iterator naar een array. Je hebt geen vector nodig:
In tegenstelling tot functiesjablonen in `
, werken sommige tools zoals range-for,
std::begin/
std::enden C++20 bereiken echter niet met slechts een paar iteratoren, terwijl ze wel werken met containers zoals vectoren. Het is mogelijk om een wikkelklasse voor iterator + maat te maken die zich gedraagt als een bereik, en werkt met deze gereedschappen. C++20 zal een dergelijke wrapper introduceren in de standaard bibliotheek:
std::span`.Je kunt dit niet doen met een
std::vector
zonder een kopie te maken.std::vector
is eigenaar van de pointer die het onder de motorkap heeft en wijst ruimte toe via de allocator die wordt meegeleverd.Als u toegang heeft tot een compiler die C++20 ondersteunt, kunt u gebruik maken van std::span, die precies voor dit doel is gebouwd. Het verpakt een pointer en grootte in een "container" die de C++ container interface heeft.
Zo niet, dan kunt u gsl::span gebruiken, waar de standaard versie op is gebaseerd.
Als je geen andere bibliotheek wilt importeren, kun je dit triviaal zelf implementeren, afhankelijk van welke functionaliteit je wilt hebben.
Eigenlijk zou je *kunnen gebruiken
std::vector
voor dit, door misbruik te maken van de aangepaste allocator-functionaliteit om een aanwijzer terug te geven aan het geheugen dat je wilt bekijken. Dat zou niet gegarandeerd worden door de standaard om te werken (opvulling, uitlijning, initialisatie van de geretourneerde waarden; je zou pijn moeten doen bij het toewijzen van de initiële grootte, en voor niet-primitieven zou je ook je constructeurs moeten hacken), maar in de praktijk zou ik verwachten dat het genoeg tweaks zou geven.Doe dat nooit. Het is lelijk, verrassend, hacky en onnodig. De algoritmes van de standaardbibliotheek zijn already ontworpen om zowel met ruwe arrays als met vectoren te werken. Zie de andere antwoorden voor meer informatie.
Naast de andere goede suggestie over
std::span
komen in [tag:c++20] engsl:span
, inclusief uw eigen (lichtgewicht)span
klasse tot die tijd is al makkelijk genoeg (voel je vrij om te kopiëren):U kunt een [
std::referentie_wrapper
][1] gebruiken die sinds C++11 beschikbaar is:Zoals anderen al hebben aangegeven, moet
std::vector
het onderliggende geheugen bezitten (kortom: knoeien met een aangepaste allocator) en kan dus niet worden gebruikt.Anderen hebben ook c++20's span aanbevolen, maar dat vereist natuurlijk c++20.
Ik zou de [span-lite][1] span aanbevelen. Om de ondertitel aan te halen:
Het biedt een niet-eigenaar en mutabele weergave (zoals in u kunt muteren elementen en hun volgorde, maar niet invoegen) en zoals de offerte zegt heeft geen afhankelijkheden en werkt op de meeste compilers.
Uw voorbeeld: