Come si copia il contenuto di un array in un std :: vector in C ++ senza loop?


122

Ho un array di valori che viene passato alla mia funzione da una parte diversa del programma che devo memorizzare per l'elaborazione successiva. Poiché non so quante volte verrà chiamata la mia funzione prima che sia il momento di elaborare i dati, ho bisogno di una struttura di archiviazione dinamica, quindi ho scelto un file std::vector. Non voglio dover eseguire il ciclo standard su push_backtutti i valori individualmente, sarebbe bello se potessi semplicemente copiarlo tutto usando qualcosa di simile a memcpy.

Risposte:


117

Se puoi costruire il vettore dopo aver ottenuto l'array e la dimensione dell'array, puoi semplicemente dire:

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

... supponendo che asia il tuo array e nil numero di elementi che contiene. Altrimenti, std::copy()w / resize()farà il trucco.

Starei lontano da memcpy()se non si può essere sicuri che i valori siano tipi di dati semplici (POD).

Inoltre, vale la pena notare che nessuno di questi evita davvero il ciclo for: è solo una questione se devi vederlo nel tuo codice o meno. O (n) le prestazioni di runtime sono inevitabili per la copia dei valori.

Infine, nota che gli array in stile C sono contenitori perfettamente validi per la maggior parte degli algoritmi STL: il puntatore grezzo è equivalente a begin()e ( ptr + n) è equivalente a end().


4
Il motivo per cui il ciclo e la chiamata a push_back non sono validi è perché potresti forzare il ridimensionamento del vettore più volte se l'array è abbastanza lungo.
bradtgmurray

@bradtgmurray: Penso che qualsiasi implementazione ragionevole del costruttore di vettori "due iteratori" che ho suggerito sopra chiamerebbe std :: distance () prima sui due iteratori per ottenere il numero necessario di elementi, quindi allocare solo una volta.
Drew Hall

4
@bradtgmurray: Anche push_back () non sarebbe male a causa della crescita esponenziale dei vettori (aka "tempo costante ammortizzato"). Penso che il runtime sarebbe solo nell'ordine di 2x peggiore nel peggiore dei casi.
Drew Hall

2
E se il vettore è già presente, un vec.clear (); vec.insert (vec.begin (), a, a + n); funzionerebbe anche. Quindi non avresti nemmeno bisogno che a sia un puntatore, solo un iteratore, e l'assegnazione del vettore sarebbe generale (e il modo C ++ / STL).
MP24

6
Un'altra alternativa quando non possono costrutto sarebbe assegnare : vec.assign(a, a+n), che sarebbe più compatto rispetto copia e ridimensionamento.
mMontu

209

Ci sono state molte risposte qui e quasi tutte porteranno a termine il lavoro.

Tuttavia c'è qualche consiglio fuorviante!

Ecco le opzioni:

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());
}

Per farla breve, il Metodo 4, usando vector :: insert, è il migliore per lo scenario di bsruth.

Ecco alcuni dettagli cruenti:

Il metodo 1 è probabilmente il più facile da capire. Basta copiare ogni elemento dall'array e inserirlo nella parte posteriore del vettore. Ahimè, è lento. Poiché esiste un ciclo (implicito con la funzione di copia), ogni elemento deve essere trattato individualmente; non è possibile apportare miglioramenti alle prestazioni in base al fatto che sappiamo che la matrice e i vettori sono blocchi contigui.

Il metodo 2 è un miglioramento delle prestazioni suggerito per il metodo 1; è sufficiente prenotare in anticipo la dimensione dell'array prima di aggiungerlo. Per array di grandi dimensioni questo potrebbe aiutare. Tuttavia, il miglior consiglio qui è di non usare mai la riserva a meno che la profilazione non suggerisca che potresti essere in grado di ottenere un miglioramento (o devi assicurarti che i tuoi iteratori non vengano invalidati). Bjarne è d'accordo . Per inciso, ho scoperto che questo metodo è stato il più lento per la maggior parte del tempo, anche se sto lottando per spiegare in modo completo perché era regolarmente significativamente più lento del metodo 1 ...

Il metodo 3 è la soluzione vecchia scuola: lancia un po 'di C al problema! Funziona bene e velocemente per i tipi di POD. In questo caso è necessario chiamare resize poiché memcpy funziona al di fuori dei limiti del vettore e non c'è modo di dire a un vettore che la sua dimensione è cambiata. Oltre ad essere una brutta soluzione (copia dei byte!), Ricorda che può essere usata solo per i tipi POD . Non userei mai questa soluzione.

Il metodo 4 è il modo migliore per procedere. Il suo significato è chiaro, è (di solito) il più veloce e funziona per qualsiasi oggetto. Non ci sono svantaggi nell'utilizzo di questo metodo per questa applicazione.

Il metodo 5 è un tweak sul metodo 4: copia l'array in un vettore e poi aggiungilo. Buona opzione - generalmente veloce e chiara.

Infine, sei consapevole che puoi usare i vettori al posto degli array, giusto? Anche quando una funzione si aspetta array in stile c puoi usare i vettori:

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

Spero che aiuti qualcuno là fuori!


6
Non puoi fare riferimento in modo sicuro e portabile a "& dataArray [dataArraySize]" - sta dereferenziando un puntatore / iteratore passato la fine. Invece, puoi dire dataArray + dataArraySize per ottenere il puntatore senza dover prima dereferenziarlo.
Drew Hall

2
@Drew: sì, puoi, almeno in C. È definito che &exprnon valuta expr, ne calcola solo l'indirizzo. E un puntatore uno dopo l'ultimo elemento è perfettamente valida, anche.
Roland Illig

2
Hai provato a fare il metodo 4 con 2? cioè riservando lo spazio prima dell'inserimento. Sembra che se la dimensione dei dati è grande, più inserimenti avranno bisogno di più riallocazioni. Poiché conosciamo la dimensione a priori, possiamo fare la riallocazione, prima di inserire.
Jorge Leitao

2
@ MattyT qual è lo scopo del metodo 5? Perché fare una copia intermedia dei dati?
Ruslan

2
Personalmente preferisco trarre profitto dal fatto che gli array si decadano automaticamente in puntatori: dataVec.insert(dataVec.end(), dataArray, dataArray + dataArraySize);- mi sembra molto più chiaro. Non si può ottenere nulla dal metodo 5, sembra solo piuttosto inefficiente, a meno che il compilatore non sia in grado di ottimizzare nuovamente il vettore.
Aconcagua,

37

Se tutto ciò che stai facendo è sostituire i dati esistenti, puoi farlo

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

void CopyData(int *newData, size_t count)
{
   data.assign(newData, newData + count);
}

1
Semplice da capire e sicuramente la soluzione più veloce (è solo un memcpy dietro le quinte).
Don Scott

Deta.assign è più veloce di data.insert?
Jim


10

Poiché posso modificare solo la mia risposta, creerò una risposta composita dalle altre risposte alla mia domanda. Grazie a tutti voi che avete risposto.

Usando std :: copy , questo itera ancora in background, ma non è necessario digitare il codice.

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;
}

Utilizzando memcpy regolare . Questo è probabilmente utilizzato al meglio per i tipi di dati di base (cioè int) ma non per array più complessi di strutture o classi.

vector<int> x(size);
memcpy(&x[0], source, size*sizeof(int));

Stavo per raccomandare questo approccio.
mmocny

È molto probabilmente più efficiente ridimensionare il vettore in primo piano se si conosce la dimensione in anticipo e non si utilizza back_inserter.
Luca

potresti aggiungere my_data.reserve (size)
David Nehme

Nota che internamente questo sta facendo esattamente quello che sembra che tu voglia evitare. Non sta copiando bit, sta solo ripetendo e chiamando push_back (). Immagino volessi solo evitare di digitare il codice?
mmocny

1
Perché non usi il costruttore di vettori per copiare i dati?
Martin York,

3

evita il memcpy, dico. Nessun motivo per pasticciare con le operazioni del puntatore a meno che non sia davvero necessario. Inoltre, funzionerà solo per i tipi POD (come int) ma fallirà se hai a che fare con tipi che richiedono una costruzione.


8
Forse questo dovrebbe essere un commento su una delle altre risposte, poiché in realtà non proponi una soluzione.
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 :-)

2
Sebbene questo frammento di codice sia il benvenuto e possa fornire un aiuto, sarebbe notevolmente migliorato se includesse una spiegazione di come e perché questo risolve il problema. Ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo alla persona che lo chiede ora! Si prega di modificare la risposta di aggiungere una spiegazione, e dare un'indicazione di ciò si applicano le limitazioni e le assunzioni.
Toby Speight

4
Aspetta, cosa myints?
mavavilj

2

Ancora un'altra risposta, poiché la persona ha detto "Non so quante volte verrà chiamata la mia funzione", potresti usare il metodo di inserimento vettoriale in questo modo per aggiungere matrici di valori alla fine del vettore:

vector<int> x;

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

Mi piace in questo modo perché l'implementazione del vettore dovrebbe essere in grado di ottimizzare per il modo migliore di inserire i valori in base al tipo di iteratore e al tipo stesso. Stai in qualche modo rispondendo sull'implementazione di stl.

Se hai bisogno di garantire la massima velocità e sai che il tuo tipo è un tipo POD, consiglierei il metodo di ridimensionamento nella risposta di Thomas:

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));
}

1

Oltre ai metodi presentati sopra, devi assicurarti di utilizzare std :: Vector.reserve (), std :: Vector.resize () o costruire il vettore su misura, per assicurarti che il tuo vettore abbia abbastanza elementi per conservare i tuoi dati. in caso contrario, corromperai la memoria. Questo è vero per std :: copy () o memcpy ().

Questo è il motivo per utilizzare vector.push_back (), non puoi scrivere oltre la fine del vettore.


Se stai usando un back_inserter, non è necessario prenotare in anticipo la dimensione del vettore in cui stai copiando. back_inserter esegue un push_back ().
John Dibling,

0

Supponendo che tu sappia quanto sono grandi gli elementi nel vettore:

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


Non dipende dall'implementazione di std :: vector?
ReaperUnreal

È orribile! Stai riempiendo l'array due volte, uno con '0', poi con i valori corretti. Basta fare: std :: vector <int> myArray (source, source + item_count); e fidati del tuo compilatore per produrre il memcpy!
Chris Jefferson,

Fidati del tuo compilatore per produrre __memcpy_int_aligned; dovrebbe essere ancora più veloce
MSalters
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.