Wie kopiert man den Inhalt eines Arrays in einen ++ std :: vector in C ++ ohne Schleife?

121

Ich habe ein Array von Werten, die von einem anderen Teil des Programms an meine Funktion übergeben werden, den ich für die spätere Verarbeitung speichern muss. Da ich nicht weiß, wie oft meine Funktion aufgerufen wird, bevor die Daten verarbeitet werden müssen, benötige ich eine dynamische Speicherstruktur. Daher habe ich eine ausgewählt std::vector. Ich möchte nicht die Standardschleife für push_backalle Werte einzeln ausführen müssen, es wäre schön, wenn ich alles einfach mit etwas ähnlichem kopieren könnte memcpy.

bsruth
quelle

Antworten:

116

Wenn Sie den Vektor konstruieren können, nachdem Sie das Array und die Arraygröße erhalten haben, können Sie einfach sagen:

std::vector<ValueType> vec(a, a + n);

... vorausgesetzt, es aist Ihr Array und ndie Anzahl der darin enthaltenen Elemente. Andernfalls wird std::copy()w / resize()den Trick machen.

Ich würde mich davon fernhalten, es memcpy()sei denn, Sie können sicher sein, dass es sich bei den Werten um POD-Typen (Plain-Old Data) handelt.

Erwähnenswert ist auch, dass keines davon die for-Schleife wirklich vermeidet - es ist nur eine Frage, ob Sie es in Ihrem Code sehen müssen oder nicht. O (n) Die Laufzeitleistung ist zum Kopieren der Werte unvermeidbar.

Beachten Sie schließlich, dass Arrays im C-Stil für die meisten STL-Algorithmen perfekt gültige Container sind - der Rohzeiger entspricht begin()und ( ptr + n) entspricht end().

Drew Hall
quelle
4
Der Grund, warum das Schleifen und Aufrufen von push_back schlecht ist, liegt darin, dass Sie die Größe des Vektors möglicherweise mehrmals ändern müssen, wenn das Array lang genug ist.
Bradtgmurray
@bradtgmurray: Ich denke, jede vernünftige Implementierung des oben vorgeschlagenen Vektorkonstruktors "zwei Iteratoren" würde std :: distance () zuerst für die beiden Iteratoren aufrufen, um die erforderliche Anzahl von Elementen zu erhalten, und dann nur einmal zuweisen.
Drew Hall
4
@bradtgmurray: Selbst push_back () wäre wegen des exponentiellen Wachstums von Vektoren (auch bekannt als "amortisierte konstante Zeit") nicht schlecht. Ich denke, die Laufzeit wäre im schlimmsten Fall nur in der Größenordnung von 2x schlechter.
Drew Hall
2
Und wenn der Vektor bereits vorhanden ist, wird ein vec.clear (); vec.insert (vec.begin (), a, a + n); würde auch funktionieren. Dann müssten Sie nicht einmal a als Zeiger, sondern nur als Iterator verwenden, und die Vektorzuweisung wäre allgemein fehlerhaft (und auf C ++ / STL-Weise).
MP24
6
Eine andere Alternative, wenn nicht konstruiert werden kann, wäre zuweisen : vec.assign(a, a+n), was kompakter wäre als Kopieren und Ändern der Größe.
mMontu
209

Hier gab es viele Antworten, und fast alle werden die Arbeit erledigen.

Es gibt jedoch einige irreführende Ratschläge!

Hier sind die Optionen:

vector<int> dataVec;

int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
unsigned dataArraySize = sizeof(dataArray) / sizeof(int);

// Method 1: Copy the array to the vector using back_inserter.
{
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 2: Same as 1 but pre-extend the vector by the size of the array using reserve
{
    dataVec.reserve(dataVec.size() + dataArraySize);
    copy(&dataArray[0], &dataArray[dataArraySize], back_inserter(dataVec));
}

// Method 3: Memcpy
{
    dataVec.resize(dataVec.size() + dataArraySize);
    memcpy(&dataVec[dataVec.size() - dataArraySize], &dataArray[0], dataArraySize * sizeof(int));
}

// Method 4: vector::insert
{
    dataVec.insert(dataVec.end(), &dataArray[0], &dataArray[dataArraySize]);
}

// Method 5: vector + vector
{
    vector<int> dataVec2(&dataArray[0], &dataArray[dataArraySize]);
    dataVec.insert(dataVec.end(), dataVec2.begin(), dataVec2.end());
}

Um es kurz zu machen: Methode 4 mit vector :: insert ist das Beste für das Szenario von bsruth.

Hier sind einige wichtige Details:

Methode 1 ist wahrscheinlich am einfachsten zu verstehen. Kopieren Sie einfach jedes Element aus dem Array und schieben Sie es in die Rückseite des Vektors. Leider ist es langsam. Da es eine Schleife gibt (impliziert mit der Kopierfunktion), muss jedes Element einzeln behandelt werden. Aufgrund der Tatsache, dass wir wissen, dass das Array und die Vektoren zusammenhängende Blöcke sind, können keine Leistungsverbesserungen vorgenommen werden.

Methode 2 ist eine vorgeschlagene Leistungsverbesserung gegenüber Methode 1; Reservieren Sie einfach die Größe des Arrays vor dem Hinzufügen. Für großen Arrays dies könnte helfen. Der beste Rat hier ist jedoch, niemals Reserve zu verwenden, es sei denn, die Profilerstellung deutet darauf hin, dass Sie möglicherweise eine Verbesserung erzielen können (oder Sie müssen sicherstellen, dass Ihre Iteratoren nicht ungültig werden). Bjarne stimmt zu . Übrigens stellte ich fest, dass diese Methode die meiste Zeit am langsamsten lief, obwohl ich Schwierigkeiten habe, umfassend zu erklären, warum sie regelmäßig signifikant langsamer war als Methode 1 ...

Methode 3 ist die Lösung der alten Schule - werfen Sie etwas C auf das Problem! Funktioniert gut und schnell für POD-Typen. In diesem Fall muss die Größe geändert werden, da memcpy außerhalb der Grenzen des Vektors arbeitet und es keine Möglichkeit gibt, einem Vektor mitzuteilen, dass sich seine Größe geändert hat. Abgesehen davon, dass dies eine hässliche Lösung ist (Byte-Kopieren!), Denken Sie daran, dass dies nur für POD-Typen verwendet werden kann . Ich würde diese Lösung niemals verwenden.

Methode 4 ist der beste Weg. Die Bedeutung ist klar, es ist (normalerweise) die schnellste und es funktioniert für alle Objekte. Die Verwendung dieser Methode für diese Anwendung hat keinen Nachteil.

Methode 5 ist eine Optimierung von Methode 4 - kopieren Sie das Array in einen Vektor und hängen Sie es dann an. Gute Option - im Allgemeinen schnell und klar.

Schließlich wissen Sie, dass Sie Vektoren anstelle von Arrays verwenden können, oder? Selbst wenn eine Funktion Arrays im C-Stil erwartet, können Sie Vektoren verwenden:

vector<char> v(50); // Ensure there's enough space
strcpy(&v[0], "prefer vectors to c arrays");

Hoffe das hilft jemandem da draußen!

MattyT
quelle
6
Sie können nicht sicher und portabel auf "& dataArray [dataArraySize]" verweisen - es wird dereferenziert ein Zeiger / Iterator nach dem Ende. Stattdessen können Sie dataArray + dataArraySize sagen, um den Zeiger abzurufen, ohne ihn zuerst dereferenzieren zu müssen.
Drew Hall
2
@Drew: Ja, das kannst du, zumindest in C. Es ist definiert, dass &exprnicht ausgewertet wird expr, sondern nur die Adresse davon berechnet wird. Und ein Zeiger einer nach dem letzten Element ist vollkommen gültig, auch.
Roland Illig
2
Haben Sie versucht, Methode 4 mit 2 durchzuführen? dh den Platz vor dem Einfügen reservieren. Es scheint, dass bei einer großen Datengröße für mehrere Einfügungen mehrere Neuzuweisungen erforderlich sind. Da wir die Größe a priori kennen, können wir die Neuzuweisung vor dem Einfügen vornehmen.
Jorge Leitao
2
@MattyT Was ist der Sinn von Methode 5? Warum eine Zwischenkopie der Daten erstellen?
Ruslan
2
Ich persönlich würde lieber davon profitieren, wenn Arrays automatisch zu Zeigern zerfallen: dataVec.insert(dataVec.end(), dataArray, dataArray + dataArraySize);- erscheint mir viel klarer. Kann auch mit Methode 5 nichts erreichen, sieht nur ziemlich ineffizient aus - es sei denn, der Compiler kann den Vektor wieder optimieren.
Aconcagua
37

Wenn Sie nur die vorhandenen Daten ersetzen, können Sie dies tun

std::vector<int> data; // evil global :)

void CopyData(int *newData, size_t count)
{
   data.assign(newData, newData + count);
}
Torlack
quelle
1
Einfach zu verstehen und definitiv die schnellste Lösung (es ist nur ein Memcpy hinter den Kulissen).
Don Scott
Ist deta.assign schneller als data.insert?
Jim
11

std :: copy ist das, wonach Sie suchen.

Luke
quelle
10

Da ich nur meine eigene Antwort bearbeiten kann, werde ich aus den anderen Antworten auf meine Frage eine zusammengesetzte Antwort erstellen. Vielen Dank an alle, die geantwortet haben.

Mit std :: copy wird dies immer noch im Hintergrund wiederholt, aber Sie müssen den Code nicht eingeben.

int foo(int* data, int size)
{
   static std::vector<int> my_data; //normally a class variable
   std::copy(data, data + size, std::back_inserter(my_data));
   return 0;
}

Mit normalem memcpy . Dies wird wahrscheinlich am besten für grundlegende Datentypen (dh int) verwendet, jedoch nicht für komplexere Arrays von Strukturen oder Klassen.

vector<int> x(size);
memcpy(&x[0], source, size*sizeof(int));
bsruth
quelle
Ich würde diesen Ansatz empfehlen.
mmocny
Es ist höchstwahrscheinlich effizienter, die Größe Ihres Vektors im Voraus zu ändern, wenn Sie die Größe im Voraus kennen und den back_inserter nicht verwenden.
Luke
Sie könnten my_data.reserve (Größe) hinzufügen
David Nehme
Beachten Sie, dass dies intern genau das tut, was Sie zu vermeiden scheinen. Es werden keine Bits kopiert, sondern nur eine Schleife erstellt und push_back () aufgerufen. Ich denke, Sie wollten nur vermeiden, den Code einzugeben?
mmocny
1
Verwenden Sie den Vektorkonstruktor nicht zum Kopieren der Daten?
Martin York
3

Vermeiden Sie das Memcpy, sage ich. Kein Grund, sich mit Zeigeroperationen herumzuschlagen, es sei denn, Sie müssen es wirklich tun. Außerdem funktioniert es nur für POD-Typen (wie int), schlägt jedoch fehl, wenn Sie mit Typen arbeiten, die eine Konstruktion erfordern.

Assaf Lavie
quelle
8
Vielleicht sollte dies ein Kommentar zu einer der anderen Antworten sein, da Sie eigentlich keine Lösung vorschlagen.
Finnw
3
int dataArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//source

unsigned dataArraySize = sizeof(dataArray) / sizeof(int);

std::vector<int> myvector (dataArraySize );//target

std::copy ( myints, myints+dataArraySize , myvector.begin() );

//myvector now has 1,2,3,...10 :-)
Antonio Ramasco
quelle
2
Während dieser Code - Schnipsel zu begrüßen ist, und etwas Hilfe bieten kann, würde es erheblich verbessert , wenn es eine Erklärung enthalten von , wie und warum Dies löst das Problem. Denken Sie daran, dass Sie in Zukunft die Frage für die Leser beantworten, nicht nur für die Person, die jetzt fragt! Bitte bearbeiten Sie Ihre Antwort, um eine Erklärung hinzuzufügen, und geben Sie an, welche Einschränkungen und Annahmen gelten.
Toby Speight
4
Warten Sie, was ist myints?
Mavavilj
2

Eine weitere Antwort: Da die Person sagte "Ich weiß nicht, wie oft meine Funktion aufgerufen wird", können Sie die Methode zum Einfügen von Vektoren verwenden, um Arrays von Werten an das Ende des Vektors anzuhängen:

vector<int> x;

void AddValues(int* values, size_t size)
{
   x.insert(x.end(), values, values+size);
}

Ich mag diesen Weg, weil die Implementierung des Vektors in der Lage sein sollte, den besten Weg zum Einfügen der Werte basierend auf dem Iteratortyp und dem Typ selbst zu optimieren. Sie antworten etwas auf die Implementierung von stl.

Wenn Sie die schnellste Geschwindigkeit garantieren müssen und wissen, dass Ihr Typ ein POD-Typ ist, würde ich die Methode zur Größenänderung in Thomas 'Antwort empfehlen:

vector<int> x;

void AddValues(int* values, size_t size)
{
   size_t old_size(x.size());
   x.resize(old_size + size, 0);
   memcpy(&x[old_size], values, size * sizeof(int));
}
Shane Powell
quelle
1

Zusätzlich zu den oben dargestellten Methoden müssen Sie sicherstellen, dass Sie entweder std :: Vector.reserve (), std :: Vector.resize () verwenden oder den Vektor auf Größe konstruieren, um sicherzustellen, dass Ihr Vektor genügend Elemente enthält es, um Ihre Daten zu halten. Wenn nicht, wird der Speicher beschädigt. Dies gilt entweder für std :: copy () oder memcpy ().

Dies ist der Grund für die Verwendung von vector.push_back (). Sie können nicht über das Ende des Vektors hinaus schreiben.

Thomas Jones-Low
quelle
Wenn Sie einen back_inserter verwenden, müssen Sie die Größe des Vektors, in den Sie kopieren, nicht vorreservieren. back_inserter führt einen push_back () aus.
John Dibling
0

Angenommen, Sie wissen, wie groß das Element im Vektor ist:

std::vector<int> myArray;
myArray.resize (item_count, 0);
memcpy (&myArray.front(), source, item_count * sizeof(int));

http://www.cppreference.com/wiki/stl/vector/start

Thomas Jones-Low
quelle
Hängt das nicht von der Implementierung von std :: vector ab?
ReaperUnreal
Das ist schrecklich! Sie füllen das Array zweimal, eine mit '0' und dann mit den richtigen Werten. Führen Sie einfach Folgendes aus: std :: vector <int> myArray (source, source + item_count); und vertraue darauf, dass dein Compiler das Memcpy erstellt!
Chris Jefferson
Vertrauen Sie Ihrem Compiler, dass er __memcpy_int_aligned erstellt. das sollte noch schneller sein
MSalters