Bagaimana Anda menyalin konten array ke std :: vector di C ++ tanpa perulangan?

122

Saya memiliki larik nilai yang diteruskan ke fungsi saya dari bagian lain dari program yang perlu saya simpan untuk diproses nanti. Karena saya tidak tahu berapa kali fungsi saya akan dipanggil sebelum waktunya untuk memproses data, saya memerlukan struktur penyimpanan dinamis, jadi saya memilih file std::vector. Saya tidak ingin melakukan loop standar ke push_backsemua nilai secara individual, alangkah baiknya jika saya dapat menyalin semuanya menggunakan sesuatu yang mirip dengan memcpy.

bsruth
sumber

Jawaban:

117

Jika Anda dapat membuat vektor setelah mendapatkan ukuran larik dan larik, Anda cukup mengatakan:

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

... dengan asumsi aadalah array Anda dan nmerupakan jumlah elemen yang dikandungnya. Jika tidak, std::copy()w / resize()akan melakukan trik.

Saya akan menjauh memcpy()kecuali Anda dapat yakin bahwa nilainya adalah tipe data biasa-lama (POD).

Juga, perlu dicatat bahwa tidak ada yang benar-benar menghindari perulangan for - ini hanya pertanyaan apakah Anda harus melihatnya di kode Anda atau tidak. Kinerja runtime O (n) tidak dapat dihindari untuk menyalin nilai.

Terakhir, perhatikan bahwa array gaya-C adalah container yang benar-benar valid untuk sebagian besar algoritme STL - pointer mentahnya setara dengan begin(), dan ( ptr + n) setara dengan end().

Drew Hall
sumber
4
Alasan mengapa perulangan dan pemanggilan push_back buruk adalah karena Anda mungkin memaksa vektor untuk mengubah ukuran beberapa kali jika lariknya cukup panjang.
bradtgmurray
@bradtgmurray: Saya rasa setiap implementasi yang masuk akal dari konstruktor vektor "dua iterator" yang saya sarankan di atas akan memanggil std :: distance () pertama pada dua iterator untuk mendapatkan jumlah elemen yang dibutuhkan, kemudian alokasikan hanya sekali.
Drew Hall
4
@bradtgmurray: Bahkan push_back () tidak akan terlalu buruk karena pertumbuhan eksponensial vektor (alias "waktu konstan diamortisasi"). Saya pikir runtime hanya akan berada di urutan 2x lebih buruk dalam kasus terburuk.
Drew Hall
2
Dan jika vektor sudah ada, a vec.clear (); vec.insert (vec.begin (), a, a + n); akan bekerja dengan baik. Maka Anda bahkan tidak memerlukan a untuk menjadi pointer, hanya sebuah iterator, dan tugas vektor akan menjadi failry general (dan cara C ++ / STL).
MP24
6
Alternatif lain ketika tidak dapat membangun akan menetapkan : vec.assign(a, a+n), yang akan lebih kompak dari copy & resize.
mMontu
209

Ada banyak jawaban di sini dan hampir semuanya akan menyelesaikan pekerjaan.

Namun ada beberapa nasihat yang menyesatkan!

Berikut opsinya:

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

Untuk mempersingkat cerita, Metode 4, menggunakan vector :: insert, adalah yang terbaik untuk skenario bsruth.

Berikut beberapa detail berdarah:

Metode 1 mungkin yang paling mudah dipahami. Cukup salin setiap elemen dari array dan dorong ke belakang vektor. Sayangnya, ini lambat. Karena ada loop (tersirat dengan fungsi copy), setiap elemen harus diperlakukan secara individual; tidak ada peningkatan kinerja yang dapat dibuat berdasarkan fakta bahwa kita mengetahui bahwa array dan vektor adalah blok yang berdekatan.

Metode 2 adalah peningkatan kinerja yang disarankan untuk Metode 1; cukup pra-pesan ukuran array sebelum menambahkannya. Untuk array besar, ini mungkin membantu. Namun saran terbaik di sini adalah jangan pernah menggunakan reserve kecuali profil menunjukkan Anda mungkin bisa mendapatkan peningkatan (atau Anda perlu memastikan iterator Anda tidak akan dibatalkan). Bjarne setuju . Kebetulan, saya menemukan bahwa metode ini melakukan paling lambat sebagian besar waktu meskipun aku berjuang untuk secara komprehensif menjelaskan mengapa hal itu teratur secara signifikan lebih lambat dari metode 1 ...

Metode 3 adalah solusi jadul - berikan beberapa C pada masalah ini! Bekerja dengan baik dan cepat untuk jenis POD. Dalam hal ini, pengubahan ukuran harus dipanggil karena memcpy bekerja di luar batas vektor dan tidak ada cara untuk memberi tahu vektor bahwa ukurannya telah berubah. Selain sebagai solusi yang jelek (penyalinan byte!), Ingatlah bahwa ini hanya dapat digunakan untuk jenis POD . Saya tidak akan pernah menggunakan solusi ini.

Metode 4 adalah cara terbaik untuk melakukannya. Artinya jelas, (biasanya) tercepat dan berfungsi untuk objek apa pun. Tidak ada kerugian menggunakan metode ini untuk aplikasi ini.

Metode 5 adalah tweak pada Metode 4 - salin array ke dalam vektor dan kemudian tambahkan. Pilihan bagus - umumnya cepat dan jelas.

Akhirnya, Anda sadar bahwa Anda dapat menggunakan vektor sebagai pengganti array, bukan? Bahkan ketika suatu fungsi mengharapkan array gaya-c Anda bisa menggunakan vektor:

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

Harapan yang membantu seseorang di luar sana!

MattyT
sumber
6
Anda tidak dapat dengan aman & mudah merujuk ke "& dataArray [dataArraySize]" - ini merujuk ke penunjuk / iterator sebelumnya. Sebagai gantinya, Anda dapat mengatakan dataArray + dataArraySize untuk mendapatkan pointer tanpa harus melakukan dereferensi terlebih dahulu.
Drew Hall
2
@Drew: ya, Anda bisa, setidaknya di C. Didefinisikan bahwa &exprtidak mengevaluasi expr, itu hanya menghitung alamatnya. Dan pointer satu masa lalu elemen terakhir benar-benar berlaku, juga.
Roland Illig
2
Sudahkah Anda mencoba melakukan metode 4 dengan 2? yaitu memesan ruang sebelum memasukkan. Tampaknya jika ukuran datanya besar, beberapa penyisipan akan membutuhkan beberapa realokasi. Karena kita tahu ukuran apriori, kita bisa melakukan realokasi, sebelum memasukkan.
Jorge Leitao
2
@ MattyT apa gunanya metode 5? Mengapa membuat salinan perantara dari data?
Ruslan
2
Saya pribadi lebih suka mengambil keuntungan dari array yang membusuk menjadi pointer secara otomatis: dataVec.insert(dataVec.end(), dataArray, dataArray + dataArraySize);- tampak jauh lebih jelas bagi saya. Tidak dapat memperoleh apa pun dari metode 5, hanya terlihat sangat tidak efisien - kecuali kompiler dapat mengoptimalkan vektor lagi.
Aconcagua
37

Jika yang Anda lakukan hanyalah mengganti data yang ada, maka Anda bisa melakukan ini

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

void CopyData(int *newData, size_t count)
{
   data.assign(newData, newData + count);
}
Torlack
sumber
1
Sederhana untuk dipahami dan jelas merupakan solusi tercepat (ini hanya memcpy di belakang layar).
Don Scott
Apakah deta.assign lebih cepat dari data.insert?
Jim
11

std :: copy adalah apa yang Anda cari.

luke
sumber
10

Karena saya hanya dapat mengedit jawaban saya sendiri, saya akan membuat jawaban gabungan dari jawaban lain atas pertanyaan saya. Terima kasih untuk semua yang menjawab.

Menggunakan std :: copy , ini masih berulang di latar belakang, tetapi Anda tidak perlu mengetikkan kodenya.

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

Menggunakan memcpy biasa . Ini mungkin paling baik digunakan untuk tipe data dasar (yaitu int) tetapi tidak untuk array struct atau class yang lebih kompleks.

vector<int> x(size);
memcpy(&x[0], source, size*sizeof(int));
bsruth
sumber
Saya akan merekomendasikan pendekatan ini.
mmocny
Kemungkinan besar lebih efisien untuk mengubah ukuran vektor Anda terlebih dahulu jika Anda mengetahui ukurannya sebelumnya, dan tidak menggunakan back_inserter.
Lukas
Anda dapat menambahkan my_data.reserve (ukuran)
David Nehme
Perhatikan bahwa secara internal ini melakukan persis apa yang tampaknya ingin Anda hindari. Ini bukan menyalin bit, itu hanya perulangan dan memanggil push_back (). Saya kira Anda hanya ingin menghindari mengetik kode?
mmocny
1
Apakah Anda tidak menggunakan konstruktor vektor untuk menyalin data?
Martin York
3

hindari memcpy, kataku. Tidak ada alasan untuk mengacaukan operasi penunjuk kecuali Anda benar-benar harus melakukannya. Selain itu, ini hanya akan berfungsi untuk jenis POD (seperti int) tetapi akan gagal jika Anda berurusan dengan jenis yang memerlukan konstruksi.

Assaf Lavie
sumber
8
Mungkin ini harus menjadi komentar di salah satu jawaban lain, karena Anda sebenarnya tidak mengajukan solusi.
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
sumber
2
Meskipun cuplikan kode ini diterima, dan mungkin memberikan beberapa bantuan, akan sangat ditingkatkan jika menyertakan penjelasan tentang bagaimana dan mengapa ini menyelesaikan masalah. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, bukan hanya orang yang bertanya sekarang! Harap edit jawaban Anda untuk menambahkan penjelasan, dan berikan indikasi batasan dan asumsi apa yang berlaku.
Toby Speight
4
Tunggu apa myints?
mavavilj
2

Namun jawaban lain, karena orang tersebut berkata "Saya tidak tahu berapa kali fungsi saya akan dipanggil", Anda dapat menggunakan metode penyisipan vektor seperti itu untuk menambahkan array nilai ke akhir vektor:

vector<int> x;

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

Saya suka cara ini karena implementasi vektor harus dapat mengoptimalkan cara terbaik untuk memasukkan nilai berdasarkan tipe iterator dan tipe itu sendiri. Anda agak membalas penerapan stl.

Jika Anda perlu menjamin kecepatan tercepat dan Anda tahu tipe Anda adalah tipe POD maka saya akan merekomendasikan metode pengubahan ukuran dalam jawaban 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));
}
Shane Powell
sumber
1

Selain metode yang disajikan di atas, Anda perlu memastikan bahwa Anda menggunakan std :: Vector.reserve (), std :: Vector.resize (), atau buat vektor ke ukuran, untuk memastikan vektor Anda memiliki cukup elemen itu untuk menyimpan data Anda. jika tidak, Anda akan merusak memori. Ini berlaku untuk std :: copy () atau memcpy ().

Ini adalah alasan untuk menggunakan vector.push_back (), Anda tidak bisa menulis melewati akhir vektor.

Thomas Jones-Low
sumber
Jika Anda menggunakan back_inserter, Anda tidak perlu memesan sebelumnya ukuran vektor yang Anda salin. back_inserter melakukan push_back ().
John Dibling
0

Dengan asumsi Anda tahu seberapa besar item dalam vektor adalah:

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
sumber
Bukankah itu tergantung pada implementasi std :: vector?
ReaperUnreal
Mengerikan! Anda mengisi array dua kali, satu dengan '0, lalu dengan nilai yang benar. Lakukan saja: std :: vector <int> myArray (source, source + item_count); dan percayakan kompiler Anda untuk menghasilkan memcpy!
Chris Jefferson
Percayai kompilator Anda untuk menghasilkan __memcpy_int_aligned; yang seharusnya lebih cepat
MSalters