Vector :: resize () ve vector :: rezerv () arasında seçim


158

Bir vectorüye değişkenime bir miktar bellek önceden tahsis ediyorum . Aşağıdaki kod minimum kısımdır

class A {
  vector<string> t_Names;
public:
  A () : t_Names(1000) {}
};

Şimdi bir noktada, eğer t_Names.size()eşitse 1000. Boyutu artırmayı planlıyorum 100. Sonra ulaşırsa 1100, tekrar artırın 100ve böyle devam edin.

Sorum şu, vector::resize()ve arasında ne seçeceğim vector::reserve(). Bu tür bir senaryoda daha iyi bir seçim var mı?

Düzenleme : için kesin bir tahminim var t_Names. Bunun etrafında olmak tahmin 700etmek 800. Bununla birlikte, belirli (nadiren) durumlarda, bundan daha fazla büyüyebilir 1000.


34
Bunu yapmanın, vektör büyümesinin artık sabit zamanda amorti edilmediği ve kullanmanın performans avantajlarından birini kaybettiğiniz anlamına geldiğini fark edersiniz std::vector.
Blastfurnace

1
İlgili, Dr. Dobbs sitesinde C ++ Daha Kolay: Vektörler Nasıl Büyüyor konusuna bakın .
jww

Yanıtlar:


267

İki işlev çok farklı şeyler yapar!

resize()(Kendi değerini belirlemek için isteğe bağlı ikinci bir argüman) yöntemi (ve yapıcısına argüman geçen eşdeğerdir) ekler veya vektöre elemanların silme uygun sayıda bu verilen boyuta getirmek için. Bunu etkileyecek size(), yineleme tüm bu öğelerin üzerinden geçecek, push_back onlardan sonra eklenecek ve operator[].

reserve()Yöntem yalnızca bellek ayırır, ancak yapraklar o başlatılmamış. Yalnızca etkiler capacity(), ancak size()değişmeyecektir. Vektöre hiçbir şey eklenmediği için nesneler için bir değer yoktur. Daha sonra öğeleri eklerseniz, önceden yapıldığından yeniden tahsis olmayacaktır, ancak tek sonuç budur.

Yani ne istediğine bağlı. 1000 varsayılan öğe dizisi istiyorsanız, kullanın resize(). 1000 öğe eklemeyi beklediğiniz bir dizi istiyorsanız ve birkaç ayırmadan kaçınmak istiyorsanız, kullanın reserve().

DÜZENLEME: Blastfurnace'in yorumu, soruyu tekrar okumama ve sizin durumunuzda doğru cevabın manuel olarak önceden tahsis edilmediğini anlamamı sağladı . İhtiyacınız olan öğeleri sonuna kadar eklemeye devam edin. Vektör gerektiğinde otomatik olarak yeniden tahsis edecek ve bunu belirtilen manuel yoldan daha verimli bir şekilde yapacaktır . reserve()Mantıklı olduğu tek durum , önceden kolayca erişebileceğiniz toplam boyut için makul ölçüde kesin bir tahminde bulunmanızdır.

DÜZENLEME2: Reklam sorusu düzenleme: İlk tahmininiz varsa, o zaman reserve()bu tahmininiz. Yeterli olmadığı ortaya çıkarsa, bırakın vektör bir şeyi yapsın.


Soruyu düzenledim. İçin kesin bir tahminim var vector.
iammilind

3
@Jan: Peki, gerekli mülkü korumayı kendiniz için ne kadar zorlaştırdığınıza göre kırılgan ya da değil. Gibi bir şey x.reserve(x.size() + newdata); vector<int>::iterator special_element = get_special_element(x); for (int i = 0; i < newdata; ++i) { if some_function(i, special_element) x.push_back(i); }, yer ayırmak söz konusu olduğunda oldukça sağlamdır. Gerçekte kaç elementin ekleneceği hakkında hiçbir fikrim yok, ancak bir üst sınırım var. Elbette şüphe duyduğunuzda, vektörlerde yineleyiciler yerine sadece indeksler kullanabilirsiniz, fark genellikle göz ardı edilebilir.
Steve Jessop

5
İfadeleriniz, doğru cevabın zaten farkında olan biri için anlamlıdır, ancak soruyu sorması gereken insanları kolayca yanlış yönlendirebilir. "resize () ... vektöre belirli sayıda öğe ekler" - yalnızca ilk kullanıldığında doğrudur - genellikle istenen sayı ile önceden var olan arasındaki farkı ekler size(). "Reserve () yöntemi yalnızca bellek ayırır" - capacity()zaten yeterli olup olmadığına bağlı olarak bellek ayırabilir veya ayırmayabilir , ayrıca öğeleri hareket ettirmesi ve orijinal belleğini serbest bırakması gerekebilir. "birkaç tahsisattan kaçınmak istiyorum" ve kopyalar vb.
Tony Delroy

21
Aslında, zorlamadan önce ayırmak çok önemlidir ve kullanılmalıdır. Bir tür 3B model yükleyici kodladığınızı ve modelin 15000 köşesi olduğunu varsayalım. Yüklerken her bir tepe noktasını önceden ayırmadan geri itmeye çalışırsanız, ciddi zaman alacaktır. Ben şahsen yaşadım, 100000'e yakın köşeli bir car .obj modelini yüklemeye çalıştım, 30 saniye sürdü. Daha sonra .reserve () ile ön-ayırmayı kullanarak kodu yeniden düzenledim, şimdi 3 saniye sürüyor. Sadece kodun başına bir .reserve (100000) koyarak 27 saniye kazandık.
deniz

1
@deniz Bu 100000 ölçeğinde önemsiz bir gerçek, ancak 100-300 ölçeğinde çok doğru değil, burada rezerve etme gereksiz yere yapılırsa israf olabilir.
deworde

31

resize()sadece bellek ayırmakla kalmaz, aynı zamanda argüman olarak aktardığınız istenen boyut kadar çok örnek oluşturur . Ancak yalnızca bellek ayırır, örnekler oluşturmaz. Yani,resize()reserve()

std::vector<int> v1;
v1.resize(1000); //allocation + instance creation
cout <<(v1.size() == 1000)<< endl;   //prints 1
cout <<(v1.capacity()==1000)<< endl; //prints 1

std::vector<int> v2;
v2.reserve(1000); //only allocation
cout <<(v2.size() == 1000)<< endl;   //prints 0
cout <<(v2.capacity()==1000)<< endl; //prints 1

Çıktı ( çevrimiçi demo ):

1
1
0
1

Yani resize()varsayılan oluşturulan nesneler istemiyorsanız, arzu olmayabilir. Aynı zamanda yavaş olacak. Ayrıca, push_back()ona yeni elemanlar size()koyarsanız, vektörün yeni bellek tahsis ederek daha da artacaktır (bu aynı zamanda mevcut elemanların yeni tahsis edilen bellek alanına taşınması anlamına gelir). reserve()Başlangıçta yeterince tahsis edilmiş hafıza olduğundan emin olmak için kullandıysanız size(), vektörün miktarı siz push_back()ona ulaştığınızda artacaktır , ancak bunun için ayırdığınız alan bitene kadar tekrar yeni hafıza tahsis etmeyecektir .


7
Yaptıktan sonra zararsız reserve(N)kullanabiliriz operator []. doğru mu?
iammilind

2
Çoğu uygulama tam olarak talep ettiğiniz miktarı tahsis edecek olsa da reserve, belirtim yalnızca en azından bu kadarını tahsis etmesini gerektirir, bu nedenle bazı uygulamalar bir sınıra yuvarlayabilir ve bu nedenle 1000'den daha yüksek kapasite gösterebilir.
Jan Hudec

17
@iammilind: Hayır, indeks büyük veya eşitse v.size(). Bunun vektörün reserve(N)değişmediğini unutmayın size().
Nawaz

6
@iammilind: Yanlış. RESERVE çağrıldıktan sonra hiçbir girdi eklenmez, sadece bunları eklemek için yeterli bellek elde edilir.
Jan Hudec

2

Açıklamanızdan, t_Names vektörünün ayrılmış depolama alanını "rezerve etmek" istediğiniz gibi görünüyor.

resizeYeni tahsis edilen vektörü reservesadece tahsis ettiği ancak inşa etmediği yerde başlattığınıza dikkat edin . Bu nedenle, "ayır", "yeniden boyutlandır" dan çok daha hızlıdır

Yeniden boyutlandırma ve ayırma farkı ile ilgili belgelere bakabilirsiniz.


1
Lütfen bunun yerine buraya bakın: vektör ve kapasite ( neden? )
sehe

1
Bağlantı eklediğiniz için teşekkürler, sehe
dip

2

rezerve edildiğinde nesnelerin başlatılmasını istemediğinizde ayırın. ayrıca, yeniden boyutlandırdığınızda, kullanım sayısına karşı mantıksal olarak ayırt etmeyi ve izlemeyi tercih edebilirsiniz. bu nedenle arayüzde davranışsal bir fark vardır - vektör, rezerve edildiğinde aynı sayıda öğeyi temsil edecek ve senaryonuzda yeniden boyutlandırıldığında 100 öğe daha büyük olacaktır.

Bu tür bir senaryoda daha iyi bir seçim var mı?

Varsayılan davranışla savaşırken tamamen sizin amaçlarınıza bağlıdır. bazı insanlar özelleştirilmiş ayırıcıları tercih eder - ancak size iyi tavsiyelerde bulunmak için programınızda çözmeye çalıştığınız şeyin ne olduğuna dair daha iyi bir fikre ihtiyacımız var.

fwiw, birçok vektör uygulaması büyümeleri gerektiğinde tahsis edilen eleman sayısını ikiye katlar - en yüksek tahsis boyutlarını en aza indirmeye mi çalışıyorsunuz yoksa bazı kilitsiz program veya başka bir şey için yeterli alan ayırmaya mı çalışıyorsunuz?


" Nesnelerin rezerve edildiğinde başlatılmasını istemediğinizde ayırın. " Doğru formülasyon, nesnelerin var olmasını istemediğiniz zamandır . Nesnelerin okunamadığı, ancak atanabildiği, önemsiz olarak yapılandırılabilir bir türden başlatılmamış bir dizi gibi değildir; daha ziyade, yalnızca bellek ayrılmıştır, ancak içinde hiçbir nesne yoktur, bu nedenle bunlara operator[]veya herhangi bir şey kullanılarak erişilemez .
underscore_d
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.