szybki sposób na skopiowanie jednego wektora do drugiego


155

Wolę dwa sposoby:

void copyVecFast(const vec<int>& original)
{
  vector<int> newVec;
  newVec.reserve(original.size());
  copy(original.begin(),original.end(),back_inserter(newVec));
}

void copyVecFast(vec<int>& original)
{

  vector<int> newVec;
  newVec.swap(original); 
}

Jak ty to robisz?


14
Drugi ma mylącą nazwę - bo nie jest kopią (choć jest szybki).
Anonimowy

Odpowiedzi:


126

Twój drugi przykład nie zadziała, jeśli wyślesz argument przez odwołanie. Miałeś na myśli

void copyVecFast(vec<int> original) // no reference
{

  vector<int> new_;
  new_.swap(original); 
}

To by zadziałało, ale jest łatwiejszy sposób

vector<int> new_(original);

Dobrze, to działa. Ale to nie działa dla tablicy wektorów: na przykład: wektor <int> A [n];
ABcDexter

8
To jest zamiana, a nie kopiowanie.
sdd

1
@sdd - nie, nie jest. Sprawdź listę argumentów. originaljest kopią argumentu funkcji.
rlbond

249

Ale to nie to samo, prawda? Jedna to kopia, druga to zamiana . Stąd nazwy funkcji.

Mój ulubiony to:

a = b;

Gdzie ai bsą wektory.


3
W rzeczywistości podejście to przekazuje wartość, kompilator wywołuje konstruktor kopiujący, a następnie zamienia nowo utworzony element. Dlatego rlbond sugeruje bezpośrednie wywołanie konstruktora kopiującego, aby osiągnąć ten sam efekt.
David Rodríguez - dribeas

1
Jednak nie możesz wywołać rlbon bez funkcji, która przekazuje oryginał jako val. W przeciwnym razie oryginalny będzie pusty. Drugie rozwiązanie zapewniło, że zawsze będziesz dzwonić według wartości, dzięki czemu nie stracisz daty w oryginalnym wektorze. (Zakładając, że zamiana dotyczy wskaźników)
Eyad Ebrahim

Czy nie spowoduje to przeniesienia elementów b do a (pozostawiając b o rozmiarze == 0)?
Jonathan.

1
@Jonathan. Zakładając, że o tym mówisz, a = bnie. Przypisanie oznacza: awyrównać bbez zmiany b. W przeciwieństwie do tego, std::swap(a, b)by wymieniać ich zawartości (tak b's sizebędzie teraz co ajest przedtem). Być może myślisz o operacji przenoszenia (jak ma to miejsce w C ++ 11, ale nie w zwykłym zadaniu, takim jak to). Taki ruch pozostawiłby bw, hm, „interesującym” stanie - patrz stackoverflow.com/questions/17730689/…
Daniel Earwicker

1
@Jonathan. Zwróć uwagę na podwójny ampersand &&. Ta wersja będzie używana tylko jako odniesienie do wartości r. Nie będzie pasować do żadnej wartości innej niż stała (tak jak bw moim przykładzie powyżej). Możesz zamienić się bw jeden, mówiąc: a = std::move(b);Zobacz en.cppreference.com/w/cpp/language/value_category, aby uzyskać jeszcze wyższy poziom złożoności.
Daniel Earwicker,

74

To kolejny ważny sposób na wykonanie kopii wektora, po prostu użyj jego konstruktora:

std::vector<int> newvector(oldvector);

Jest to nawet prostsze niż użycie std::copyprzejścia całego wektora od początku do końca do std::back_insertnowego wektora.

To powiedziawszy, twój .swap()jeden nie jest kopią, zamiast tego zamienia dwa wektory. Zmodyfikowałbyś oryginał, aby już nic nie zawierał! Który nie jest kopią.


19

Bezpośrednia odpowiedź:

  • Użyj =operatora

Możemy użyć publicznej funkcji członkowskiej std::vector::operator=kontenera std::vectordo przypisania wartości z jednego wektora do innego.

  • Użyj funkcji konstruktora

Poza tym funkcja konstruktora również ma sens. Funkcja konstruktora z innym wektorem jako parametrem (np. x) Konstruuje kontener z kopią każdego elementu w x, w tej samej kolejności.

Uwaga:

  • Nie używać std::vector::swap

std::vector::swapto nie kopiowanie wektora do innego, w rzeczywistości zamiana elementów dwóch wektorów, tak jak sugeruje jego nazwa. Innymi słowy, wektor źródłowy do skopiowania jest modyfikowany po std::vector::swapwywołaniu, co prawdopodobnie nie jest tym, czego się oczekuje.

  • Głęboka czy płytka kopia?

Jeśli elementy w wektorze źródłowym są wskaźnikami do innych danych, czasami potrzebna jest głęboka kopia.

Według Wikipedii:

Głęboka kopia, co oznacza, że ​​pola są wyłuskiwane: zamiast odniesień do kopiowanych obiektów tworzone są nowe obiekty kopii dla wszystkich obiektów, do których istnieją odniesienia, a odniesienia do tych umieszczane są w B.

W rzeczywistości nie ma obecnie wbudowanego sposobu w C ++, aby wykonać głęboką kopię. Wszystkie wymienione powyżej sposoby są płytkie. Jeśli konieczna jest głęboka kopia, możesz przejść przez wektor i ręcznie skopiować odniesienia. Alternatywnie do przechodzenia można rozważyć iterator. Dyskusja na temat iteratora wykracza poza to pytanie.

Bibliografia

Strona w std::vectorwitrynie cplusplus.com


14

nie powinieneś używać zamiany do kopiowania wektorów, zmieniłoby to "oryginalny" wektor.

zamiast tego przekazać oryginał jako parametr do nowego.


14
new_vector.assign(old_vector.begin(),old_vector.end()); // Method 1
new_vector = old_vector; // Method 2

-14

W przypadku, gdy wektor JUŻ istniał i chciałeś po prostu skopiować, możesz zrobić to:

newVec.resize(oldVec.size());
memcpy(&newVec.at(0), &oldVec.at(0), oldVec.size());

1
Proszę, nie memcpy. Również to nie zadziała, ponieważ memcpy przyjmuje rozmiar w bajtach. Jeśli inny wektor już istnieje, możesz po prostu zrobić newVec = oldVecto samo, co jedna z innych odpowiedzi.
FDinoff

Tak masz rację. Nie widziałem tego. @FDinoff, chociaż poniżej jeden działa, dlaczego sugerujesz nie używać memcpy? Wydaje się, że jest znacznie szybszy niż newVec = oldVec. memcpy (& newVec.at (0), & oldVec.at (0), oldVec.size () * sizeof (int));
sgowd

1
W ogólnym przypadku kopiowanie obiektu bez wywoływania jego konstruktora kopiującego może prowadzić do drobnych błędów. W tym przypadku pomyślałbym, że mieliby taki sam występ. Gdyby tak nie było, powiedziałbym, że wektor nie był zoptymalizowany pod kątem wydajności, ponieważ powinien już to robić. Czy faktycznie napisałeś benchmark?
FDinoff

Nie krytykowałem cię. Mój lider zespołu też zasugerował to samo i starałem się to zrozumieć.
sgowd

(Nie sądziłem, że mnie krytykujesz). Czy jest jeszcze coś, czego nie rozumiesz?
FDinoff
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.