C stili diziden std :: vector nasıl başlatılır?


174

std::vectorC stili bir diziden a başlatmanın en ucuz yolu nedir ?

Örnek: Aşağıdaki sınıfta, bir var vector, ancak dış kısıtlamalar nedeniyle, veri C stili dizi olarak geçirilir:

class Foo {
  std::vector<double> w_;
public:
  void set_data(double* w, int len){
   // how to cheaply initialize the std::vector?
}

Açıkçası, ben arayabilir w_.resize()ve daha sonra öğeleri üzerinde döngü veya çağrı std::copy(). Daha iyi yöntemler var mı?


11
Sorunun temel noktası, vektörün C-tarzı dizinizi oluşturmak için aynı ayırıcı kullanılıp kullanılmadığını bilmesinin bir yolu olmamasıdır. Bu şekilde vektör, kendi ayırıcısını kullanarak bellek tahsis etmelidir. Aksi takdirde, temel diziyi değiştirebilir ve dizinizle değiştirebilir.
Void

Yanıtlar:


233

İşaretçileri yineleyici olarak kullanabileceğinizi unutmayın:

w_.assign(w, w + len);

3
Bu bir uygulama kalitesi sorunudur. Yineleyiciler kategorilerini belirten etiketlere sahip olduğundan, bir uygulamasının assignoptimizasyonu için bunları kullanmak kesinlikle ücretsizdir; en azından VC ++ 'da, gerçekten de bunu yapar.
Pavel Minaev

38
Hızlı çözüm std :: vector <double> w_ (w, w + len);
sıkışma

1
Bu, öğeleri 'w_' için yeni oluşturulan bir depolama alanına kopyalar; 'w_.data', 'w'yi göstermez. Hala 'w' yi ayırmalısın. Mülkiyet devri yok
Indy9000

1
Eğer sonun ötesinde bir eleman varsa , iyi olmalıdır (tıpkı v.end()benzer bir durumda vektörle sondan bir sonraki bir yineleyici gibi ). Bir iddiada bulunursanız, başka bir yerde bir şey kapalıdır.
Pavel Minaev

1
Sadece hızlı bir şekilde, vektör kapsam dışına çıktığında dizi belleğini yeniden konumlandıracak mı?
Adrian Koch

41

Başlatma kelimesini kullanırsanız, bu bir kerelik bir atama mı yoksa birden çok kez mi olabileceği belirsizdir.

Yalnızca bir kez başlatmaya ihtiyacınız varsa, yapıcıya koyabilir ve iki yineleyici vektör yapıcısını kullanabilirsiniz:

Foo::Foo(double* w, int len) : w_(w, w + len) { }

Aksi takdirde, daha önce önerildiği gibi atamayı kullanın:

void set_data(double* w, int len)
{
    w_.assign(w, w + len);
}

1
Benim durumumda, ödev tekrar tekrar gerçekleşecek.
Frank

12

Dizinin boyutunu otomatik olarak 'öğrenebilirsiniz':

template<typename T, size_t N>
void set_data(const T (&w)[N]){
    w_.assign(w, w+N);
}

Umarım, arayüzü yukarıdaki gibi set_data olarak değiştirebilirsiniz. Hala ilk argüman olarak C stili bir diziyi kabul etmektedir. Sadece referans olarak alır.


Nasıl çalışır

[Güncelleme: Boyutu öğrenme hakkında daha kapsamlı bir tartışma için buraya bakın ]

İşte daha genel bir çözüm:

template<typename T, size_t N>
void copy_from_array(vector<T> &target_vector, const T (&source_array)[N]) {
    target_vector.assign(source_array, source_array+N);
}

Bu, dizi bir diziye başvuru olarak geçirildiği için çalışır. C / C ++ 'da, bir diziyi işlev olarak geçiremezsiniz, bunun yerine bir işaretçiye bozulur ve boyutu kaybedersiniz. Ama C ++, sen yapabilirsiniz diziye bir başvuru geçmektedir.

Bir diziyi başvuru ile iletmek, türlerin tam olarak eşleşmesini gerektirir. Bir dizinin boyutu türünün bir parçasıdır. Bu, bizim için boyutu öğrenmek için N şablon parametresini kullanabileceğimiz anlamına gelir.

Bir vektör döndüren bu işleve sahip olmak daha da basit olabilir. Geçerli uygun derleyici optimizasyonları ile, bu göründüğünden daha hızlı olmalıdır.

template<typename T, size_t N>
vector<T> convert_array_to_vector(const T (&source_array)[N]) {
    return vector<T>(source_array, source_array+N);
}

1
Son örnekte return { begin(source_array), end(source_array) };de mümkündür
MM

12

Pavel yakındı, ancak ac stili dizisinden sıralı bir kapsayıcı başlatmak için daha basit ve zarif bir çözüm bile var.

Senin durumunda:

w_ (array, std::end(array))
  • dizi bize dizinin başına bir işaretçi getirir (adını yakalamadı),
  • std :: end (array) bize dizinin sonuna bir yineleyici getirecektir.

1
Bu hangi C ++ sürümünü / sürümünü gerektirir?
Vlad

1
Bu, en az c ++ 98'den itibaren std :: vector yapıcılarından biridir .... Buna 'aralık yapıcısı' denir. cplusplus.com/reference/vector/vector/vector Deneyin.
Mugurel

1
Daha bağımsız sürüm: w_ (std :: begin (dizi), std :: end (dizi)); (Gelecekte bir C ++ kapsayıcısının C dizisini değiştirebilirsiniz).
Andrew Romanov

13
Dikkat edin, bu yalnızca gerçek array( eğer genel veya yerel (geçerli işlevde bildirilen) diziden kopyaladığınız anlamına gelir) varsa çalışır. OP durumunda, bir işaretçi ve bir uzunluk alıyor ve uzunluğa ayarlanmadığı için, boyutlandırılmış bir diziye veya herhangi bir şeye bir işaretçi almaya std::endgeçemiyorlar , bu yüzden işe yaramayacak.
ShadowRanger

2
vectoraşırı yüklenmez operator(), bu yüzden derlenmez. std::endbir işaretçi üzerinde çağrılmanın da bir yararı yoktur (soru bir işaretçi ve ayrı bir uzunluk değişkeninden bir vektör atamasını ister).
MM

2

Hızlı genel cevap:

std::vector<double> vec(carray,carray+carray_size); 

veya soruya özel:

std::vector<double> w_(w,w+len); 

Yukarıdakilere dayanarak : İşaretleyicilere yineleyici olarak davranabileceğinizi unutmayın


0

std::vector<double>::assignçünkü bu küçük bir kod . Ama aslında nasıl çalışır? Yeniden boyutlandırıldıktan sonra kopyalanmıyor mu? MS STL uygulamasında tam olarak bunu kullanıyorum.

Korkarım ki başlatmayı (yeniden) uygulamanın daha hızlı bir yolu yokturstd::vector .


vektör ile dizi arasında paylaşılacak veriler olursa ne olur? Bu durumda bir şey kopyalamamız gerekiyor mu?
Vlad

bu bir cevap mı yoksa soru mu? mevcut cevaplara ne getiriyor?
Jean-François Fabre

@ Jean-FrançoisFabre ve yorumunuz ne getiriyor? ;) doğru, çağlar önce verilen kötü bir cevap.
Janusz Lenar
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.