Bir dizinin içeriğini döngü yapmadan C ++ 'da bir std :: vektörüne nasıl kopyalarsınız?


122

Daha sonraki işlemler için kaydetmem gereken programın farklı bir bölümünden işlevime aktarılan bir dizi değerim var. Verileri işleme zamanı gelmeden fonksiyonumun kaç kez çağrılacağını bilmediğim için dinamik bir depolama yapısına ihtiyacım var, bu yüzden bir std::vector. Standart döngüyü push_backtüm değerlere tek tek yapmak zorunda kalmak istemiyorum, hepsini benzer bir şey kullanarak kopyalayabilseydim güzel olurdu memcpy.

Yanıtlar:


117

Dizi ve dizi boyutunu aldıktan sonra vektörü oluşturabilirseniz, şunu söyleyebilirsiniz:

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

... asizin diziniz ve niçerdiği elemanların sayısı olduğunu varsayar . Aksi takdirde, std::copy()ağırlık / resize()hile olacaktır.

memcpy()Değerlerin düz eski veri (POD) türleri olduğundan emin olmadığınız sürece uzak dururum.

Ayrıca, bunların hiçbirinin for döngüsünü gerçekten engellemediğini de belirtmek gerekir - bu sadece onu kodunuzda görmeniz gerekip gerekmediğiyle ilgili bir soru. O (n) çalışma zamanı performansı, değerleri kopyalamak için kaçınılmazdır.

Son olarak, C-stili dizilerin çoğu STL algoritması için mükemmel şekilde geçerli kaplar olduğunu unutmayın - ham işaretçi eşdeğerdir begin()ve ( ptr + n) ile eşdeğerdir end().


4
Döngünün ve push_back çağrısının kötü olmasının nedeni, dizi yeterince uzunsa vektörü birden çok kez yeniden boyutlandırmaya zorlayabilmenizdir.
bradtgmurray

@bradtgmurray: Yukarıda önerdiğim "iki yineleyici" vektör kurucusunun mantıklı bir uygulamasının, gerekli sayıda öğeyi elde etmek için önce iki yineleyicide std :: distance () 'yı çağıracağını, ardından yalnızca bir kez ayıracağını düşünüyorum.
Drew Hall

4
@bradtgmurray: Push_back () bile vektörlerin üstel büyümesi nedeniyle ("amorti edilmiş sabit zaman") çok kötü olmaz. Bence çalışma zamanı en kötü durumda sadece 2 kat daha kötü olurdu.
Drew Hall

2
Ve vektör zaten oradaysa, bir vec.clear (); vec.insert (vec.begin (), a, a + n); de işe yarar. O zaman a'nın bir işaretçi, sadece bir yineleyici olmasını bile gerektirmezsiniz ve vektör ataması başarısız genel (ve C ++ / STL yolu) olur.
MP24

6
İnşa edilemediğinde başka bir alternatif atama olacaktır : bu vec.assign(a, a+n), kopyala ve yeniden boyutlandırmaktan daha kompakt olacaktır.
mMontu

209

Burada birçok cevap var ve hemen hemen hepsi işi halledecek.

Ancak bazı yanıltıcı tavsiyeler var!

Seçenekler şunlardır:

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

Uzun lafın kısası Yöntem 4, vektör :: insert kullanmak, bsruth'un senaryosu için en iyisidir.

İşte bazı kanlı ayrıntılar:

Yöntem 1 muhtemelen anlaşılması en kolay olanıdır. Dizideki her bir öğeyi kopyalayın ve vektörün arkasına itin. Ne yazık ki, yavaş. Bir döngü olduğu için (kopyalama işleviyle ifade edilir), her öğe ayrı ayrı ele alınmalıdır; Dizi ve vektörlerin bitişik bloklar olduğunu bildiğimiz gerçeğine dayanarak hiçbir performans iyileştirmesi yapılamaz.

Yöntem 2 , Yöntem 1 için önerilen bir performans iyileştirmesidir; diziyi eklemeden önce boyutunu önceden ayırmanız yeterlidir. Büyük diziler için bu yardımcı olabilir . Bununla birlikte, buradaki en iyi tavsiye, profil oluşturma bir iyileştirme elde edebileceğinizi (veya yineleyicilerinizin geçersiz kılınmayacağından emin olmanız gerektiğini) önermedikçe, asla rezerv kullanmamaktır. Bjarne aynı fikirde . Bu arada, neden düzenli olarak yöntem 1'den önemli ölçüde daha yavaş olduğunu kapsamlı bir şekilde açıklamakta zorlansam da , bu yöntemin çoğu zaman en yavaş performans gösterdiğini buldum ...

Yöntem 3 eski okul çözümüdür - soruna biraz C at! POD türleri için iyi ve hızlı çalışır. Memcpy vektörün sınırları dışında çalıştığından ve bir vektöre boyutunun değiştiğini söylemenin bir yolu olmadığından bu durumda yeniden boyutlandırmanın çağrılması gerekir. Çirkin bir çözüm olmasının yanı sıra (bayt kopyalama!), Bunun yalnızca POD türleri için kullanılabileceğini unutmayın . Bu çözümü asla kullanmam.

Yöntem 4 , gitmenin en iyi yoludur. Anlamı açıktır, (genellikle) en hızlısıdır ve herhangi bir nesne için işe yarar. Bu uygulama için bu yöntemi kullanmanın bir dezavantajı yoktur.

Yöntem 5 , Yöntem 4'te yapılan bir düzeltmedir - diziyi bir vektöre kopyalayın ve ardından ekleyin. İyi seçenek - genellikle hızlı ve net.

Son olarak, diziler yerine vektörleri kullanabileceğinizi biliyorsunuz, değil mi? Bir işlev c-tarzı diziler beklediğinde bile vektörleri kullanabilirsiniz:

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

Umarım bu dışarıdaki birine yardımcı olur!


6
"& DataArray [dataArraySize]" öğesine güvenli ve taşınabilir bir şekilde başvuramazsınız - bu, uçtan uca bir işaretçi / yineleyicinin başvurusunu kaldırıyor. Bunun yerine, işaretçiyi önce referansını kaldırmak zorunda kalmadan elde etmek için dataArray + dataArraySize diyebilirsiniz.
Drew Hall

2
@ Drew: evet, yapabilirsin, en azından C'de. &exprDeğerlendirmeyen expr, sadece adresini hesaplayan tanımlanmıştır . Ve son öğeyi bir geçen bir işaretçi de mükemmel bir şekilde geçerlidir.
Roland Illig

2
Yöntem 4'ü 2 ile yapmayı denediniz mi? yani yerleştirmeden önce yer ayırmak. Görünüşe göre veri boyutu büyükse, birden çok eklemenin birden çok yeniden atama yapması gerekecek. Önceden boyutu bildiğimiz için, eklemeden önce yeniden tahsisi yapabiliriz.
Jorge Leitao

2
@MattyT 5. yöntemin amacı nedir? Verilerin neden ara kopyasını almalı?
Ruslan

2
Ben şahsen, dizilerin otomatik olarak göstericilere dönüşmesinden kar etmeyi tercih ederim: dataVec.insert(dataVec.end(), dataArray, dataArray + dataArraySize);- bana çok daha net görünüyor. Yöntem 5'ten de hiçbir şey elde edilemez, yalnızca oldukça verimsiz görünüyor - derleyici vektörü yeniden optimize edemediği sürece.
Aconcagua

37

Tek yaptığınız mevcut verileri değiştirmekse, bunu yapabilirsiniz

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

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

1
Anlaşılması basit ve kesinlikle en hızlı çözüm (sadece perde arkasındaki bir hatırlatma).
Don Scott

Deta.assign, data.insert'ten daha hızlı mı?
Jim


10

Sadece kendi cevabımı düzenleyebildiğim için soruma diğer cevaplardan bileşik bir cevap vereceğim. Cevaplayan hepinize teşekkürler.

Kullanılması std :: copy bu hala arka planda yinelediğinden ama kodunu yazmanız gerekmez.

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

Normal memcpy kullanma . Bu muhtemelen en iyi temel veri türleri (yani int) için kullanılır, ancak daha karmaşık yapı veya sınıf dizileri için kullanılmaz.

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

Bu yaklaşımı tavsiye edecektim.
mmocny

Boyutu önceden biliyorsanız ve back_inserter'ı kullanmıyorsanız, vektörünüzü önceden yeniden boyutlandırmak büyük olasılıkla daha etkilidir.
luke

my_data.reserve (boyut) ekleyebilirsiniz
David Nehme

Bunun dahili olarak tam olarak kaçınmak istediğiniz şeyi yaptığını unutmayın. Bitleri kopyalamıyor, sadece döngü yapıyor ve push_back () çağırıyor. Sanırım sadece kodu yazmaktan kaçınmak istedin?
mmocny

1
Verileri kopyalamak için vektör yapıcısını kullanmıyor musunuz?
Martin York

3

Memcpy'den kaçının diyorum. Gerçekten mecbur kalmadıkça işaretçi işlemleriyle uğraşmanıza gerek yok. Ayrıca, yalnızca POD türleri (int gibi) için çalışacak, ancak inşa gerektiren türlerle uğraşıyorsanız başarısız olacaktır.


8
Belki de aslında bir çözüm önermediğiniz için bu diğer cevaplardan biri hakkında bir yorum olmalıdır.
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
Bu kod parçacığı açığız ve bazı yardım sağlamak görülebilir fakat bunun olacağını bunun bir açıklama dahil eğer büyük ölçüde geliştirilmiş bir nasıl ve niçin bu çözer sorunu. Sadece şimdi soran kişi değil, gelecekte okuyucular için soruyu cevapladığınızı unutmayın! Lütfen açıklama eklemek için cevabınızı düzenleyin ve hangi sınırlamaların ve varsayımların geçerli olduğuna dair bir gösterge verin.
Toby Speight

4
Bekle, ne var myints?
mavavilj

2

Yine başka bir cevap, kişi "Benim fonksiyonumun kaç kez çağrılacağını bilmiyorum" dediğinden, değer dizilerini vektörün sonuna eklemek için vektör ekleme yöntemini kullanabilirsiniz:

vector<int> x;

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

Bu yolu seviyorum çünkü vektör uygulaması, yineleyici türüne ve türün kendisine bağlı olarak değerleri eklemek için en iyi yolu optimize edebilmelidir. Stl'nin uygulanmasına bir şekilde yanıt veriyorsunuz.

En yüksek hızı garanti etmeniz gerekiyorsa ve türünüzün bir POD türü olduğunu biliyorsanız, Thomas'ın cevabında yeniden boyutlandırma yöntemini öneririm:

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

Yukarıda sunulan yöntemlere ek olarak, vektörünüzün içinde yeterli öğe olduğundan emin olmak için std :: Vector.reserve (), std :: Vector.resize () kullandığınızdan veya vektörü boyut olarak oluşturduğunuzdan emin olmanız gerekir. verilerinizi tutmak için. değilse, hafızayı bozarsınız. Bu, std :: copy () veya memcpy () için geçerlidir.

Vector.push_back () kullanmanın nedeni budur, vektörün sonunu yazamazsınız.


Bir back_inserter kullanıyorsanız, kopyaladığınız vektörün boyutunu önceden ayırmanıza gerek yoktur. back_inserter bir push_back () yapar.
John Dibling

0

Vektördeki öğenin ne kadar büyük olduğunu bildiğinizi varsayarsak:

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


Bu std :: vector uygulamasına bağlı değil mi?
ReaperUnreal

Bu korkunç! Diziyi iki kez, biri '0'larla, sonra da uygun değerlerle dolduruyorsunuz. Yapmanız gereken: std :: vector <int> myArray (kaynak, kaynak + öğe_sayısı); ve memcpy'i oluşturması için derleyicinize güvenin!
Chris Jefferson

Derleyicinizin __memcpy_int_aligned; bu daha da hızlı olmalı
MSalters
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.