it-swarm.com.de

STL-Vektor: Alle Elemente eines Vektors verschieben

Ich habe zwei STL-Vektoren A und B und ich möchte alle Elemente von A löschen und alle Elemente von B in A verschieben und dann B löschen. Einfach gesagt, ich möchte folgendes tun:

std::vector<MyClass> A;
std::vector<MyClass> B;
....
A = B;
B.clear();

Da B ziemlich lang sein kann, ist k*O(N) erforderlich, um diese Operation auszuführen, wobei k eine Konstante ist und Nmax(size_of(A), size_of(B)) ist. Ich habe mich gefragt, ob es einen effizienteren Weg geben könnte. Eine Sache, die ich mir vorstellen könnte, ist, A und B als Zeiger zu definieren und dann Zeiger in konstanter Zeit zu kopieren und B zu löschen.

40
aminfar

Mit C++ 11 ist das so einfach wie folgt:

A = std::move(B);

Jetzt enthält A die Elemente, die zuvor von B gehalten wurden, und B ist jetzt leer. Dadurch wird das Kopieren vermieden: Die interne Darstellung wird einfach von B nach A verschoben, sodass dies eine O(1)-Lösung ist.

Wie bei C++ 03, wie Prætorian feststellt, könnten Sie die Vektoren vertauschen. Es gibt eine Spezialisierung der std::swap-Funktion, die std::vectors als Argumente verwendet. Dadurch wird die interne Repräsentation effektiv vertauscht, sodass Sie am Ende keine Kopien der Elemente erstellen müssen, die diese enthalten. Diese Funktion funktioniert auch in O(1)-Komplexität.

65
mfontanini

Wenn Sie einen C++ 11-Compiler haben, können Sie B in A verschieben.

A = std::move(B);

Wenn Sie mit einem älteren Compiler arbeiten, swap nur die beiden

A.swap(B);

In beiden Fällen löscht die einzige Operation O(N) den Inhalt von A. Im ersten Fall erfolgt die Löschung während der Zuweisung selbst, im zweiten Fall, wenn B den Gültigkeitsbereich verlässt (da der Inhalt vertauscht wurde).

12
Praetorian

Ich habe zwei STL-Vektoren A und B, und ich möchte alle Elemente von A löschen und alle Elemente von B nach A und dann von B entfernen.

Dies kann mit einer Kombination von swap erfolgen. Tauschen Sie zuerst A und B für die erste Hälfte aus. Dann swap einen leeren std::vector<> mit B oder rufen Sie clear() auf. Der Unterschied ist, dass clear() den Speicher nicht freigibt, sondern nur die Objekte zerstört:

std::vector<int> a, b; // initialize them somehow
swap(a,b);

// clear b without releasing the memory:
std::size_t capacity = b.capacity();
b.clear();
assert(b.capacity()==capacity);

// or release the memory
std::vector<int>().swap(b);
assert(b.capacity()==0);

wenn Sie clear auf vector aufrufen, wird o(1) Zeit benötigt, da clear nichts tut, Wenn Sie wirklich B löschen möchten, nachdem Sie es A zugewiesen haben, können Sie Folgendes tun

A.swap(B);
{
    std::Vector<..> C;
    c.swap(B);
}
3
mogulkahn

Die Swap-Funktion tut dies.

#include <iostream>
#include <iterator>
#include <vector>

int main(int argc, char* argv)
{
  std::vector<int> A;
  std::vector<int> B;

  for (int i = 0; i < 10; ++i)
  {
     B.Push_back(i);
  }

  std::cout << "Before swap\n";
  std::cout << "A:";
  std::copy(A.begin(), A.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\nB:";
  std::copy(B.begin(), B.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\n";

  A.swap(B);
  B.clear();

  std::cout << "After swap\n";
  std::cout << "A:";
  std::copy(A.begin(), A.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\nB:";
  std::copy(B.begin(), B.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << "\n";
}

Die Ausgabe

Before swap
A:
B:0 1 2 3 4 5 6 7 8 9 
After swap
A:0 1 2 3 4 5 6 7 8 9 
B:
2
Jason

Wenn Sie die Vektoren nicht mit std :: move oder std :: swap austauschen können (z. B. weil A und B verwandt sind, aber unterschiedliche Typen sind, die sich möglicherweise nur durch const unterscheiden), können Sie Folgendes tun:

std::vector<MyClass>       A;
std::vector<const MyClass> B;
// ...
for( auto& a : A )
{
    B.emplace_back( std::move( a ) );
}

Man beachte, dass dies A mit der gleichen Anzahl von Elementen belässt, aber alle in einem unbestimmten Zustand sind (d. H. Sie können zugewiesen oder zerstört werden, aber nicht gelesen werden).

1
metal

std :: move funktioniert gut. Hier ist der Beispielcode für dasselbe

    vector<int> v1 = {1,2,3,10,20,30,100,200,300,999};
    vector<int> v2;

    cout << "Size of v1 before move = " << v1.size() << endl;
    cout << "Capacity of v1 before move = " << v1.capacity() << endl;

    v2 = std::move(v1);

    cout << "Size of v2 after move = " << v2.size() << endl;
    cout << "Capacity of v2 after move = " << v2.capacity() << endl;

    cout << "Size of v1 after move = " << v1.size() << endl;
    cout << "Capacity of v1 after move = " << v1.capacity() << endl;

-----------Output-------------------------
Size of v1 before move = 10
Capacity of v1 before move = 10
Size of v2 after move = 10
Capacity of v2 after move = 10
Size of v1 after move = 0
Capacity of v1 after move = 0
1
Sameer Ahuja

Es fehlt mir ein Sprecher, aber ich möchte erwähnen, dass per: https://de.cppreference.com/w/cpp/container/vector/operator%3D void.pointer recht ist. Im Speziellen...

2) Verschieben Sie den Zuweisungsoperator. Ersetzt den Inhalt durch Inhalte anderer, die die Move-Semantik verwenden (d. H. Die Daten in anderen werden von anderen in diesen Container verschoben). other ist danach in einem gültigen, aber nicht näher definierten Zustand.

Daher ist die Antwort von Praetorian per Standard falsch. Zumindest für MSVC ist dies jedoch gut genug, da die Implementierung die Liste trotzdem löscht (wahrscheinlich für die meisten zutreffend).

Interessant ist, dass, da wir einen Verschiebungskonstruktor deklarieren, kein impliziter Verschiebungszuweisungsoperator deklariert wird. Daher "wissen" wir, dass std :: vector einen Zuweisungsoperator für Operationen deklarieren muss.

0