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.