Ham Dizi Yerine Neden vector
Ham bir dizi, derleme zamanında sabitlenmiş bir boyuta sahiptir ve bir fonksiyona geçirildiği an ne kadar uzun olduğunu unutur. std::vector her iki sorunu da çözer: kendi uzunluğunu izleyen, talep üzerine büyüyen ve kendi belleğini temizleyen yeniden boyutlandırılabilir bir dizidir. Modern C++'ta vector varsayılan konteynerdir - ham bir diziye yalnızca kullanmamak için belirli bir nedenin olduğunda başvur.
<vector> ekle, ardından eleman tipini açılı parantezler içinde vererek bir tane tanımla:
scores.size() her zaman güncel uzunluğu söyler - senkron tutulacak ayrı bir int n yok ve sizeof hileleri yok. {90, 75, 100, 60} bir süslü parantez ilklendiricisidir; vector dört yuvaya ihtiyacı olduğunu kendi anlar.
Bir vector Oluşturma ve İlklendirme
Önceden ne bildiğine bağlı olarak bir tane kurmanın birkaç yolu vardır:
Parantez-süslü parantez tuzağına dikkat et: vector<int> tens(5, 10) 10 değerinden beş kopya yapar, oysa vector<int> tens{5, 10} 5 ve 10 içeren iki elemanlı bir vector yapar. Parantezler "boyut ve dolgu değeri" anlamına gelir; süslü parantezler "bu literal elemanlar" anlamına gelir.
Eleman Ekleme ve Çıkarma
Bir vector'ın bütün amacı büyümesidir. push_back sona ekler, pop_back sondan çıkarır:
back() son elemanı, front() ise ilkini döndürür - v[v.size() - 1] ve v[0]'dan daha temiz. C++11'den itibaren bir elemanı yerinde inşa etmek için emplace_back(args...) de kullanabilirsin; bu, ağır tipler için geçici bir kopyadan kaçınır.
Yaygın bir acemi hatası, boş bir vector üzerinde front() veya back() çağırmaktır. Bu bir hata değil, tanımsız davranıştır - her zaman önce if (!v.empty()) ile koru.
Eleman Okuma: [] ile at()
Bir vector'ı tıpkı bir dizi gibi [] ile indekslersin. Ancak [] sınır denetimi yapmaz - aralık dışı bir indeks tanımsız davranıştır; sessizce çöp okuyabilir veya daha sonra kafa karıştırıcı bir yerde çökebilir:
vector<int> v = {1, 2, 3};
cout << v[10]; // TANIMSIZ DAVRANIŞ - denetim yok, hata yok
Güvenlik istediğinde at() kullan. İndeksi denetler ve hatalı bir erişimde std::out_of_range fırlatır, böylece bozulma yerine net bir başarısızlık alırsın:
Pratik kural: indeksin geçerli olduğunu zaten kanıtladığın sıkı döngülerde [], hatalı girdinin sızabileceği sınırlarda ise at() kullan.
Bir vector Üzerinde Dönme
Bir vector üzerinde dolaşmanın en temiz yolu aralık tabanlı for döngüsüdür. Kopyalamadan okumak için elemanları const auto& ile al, yerinde düzenlemek için auto& ile al:
Gerçekten indekse ihtiyacın varsa (örneğin komşuları karşılaştırmak için), klasik bir sayaç döngüsü kullan - ama size()'ın işaretsiz bir tip (size_t) döndürdüğüne dikkat et. Buna karşı işaretli bir int i karşılaştırmak derleyici uyarılarına ve şaşırtıcı sarmalamalara yol açabilir; bu yüzden mümkün olduğunda size_t i ya da aralık tabanlı bir döngü tercih et:
for (size_t i = 0; i < v.size(); i++) { // size_t, int değil
cout << v[i];
}
size, capacity ve reserve
Bir vector iki sayı tutar: size() (kaç eleman içerdiği) ve capacity() (büyümek zorunda kalmadan kaç eleman tutabileceği). Bir push_back kapasiteyi aştığında vector daha büyük bir blok ayırır, her elemanı kopyalar ve eski bloğu serbest bırakır. Tekrarlanan push_back'in amortize edilmiş olarak ucuz, ama her bir yeniden ayırmanın bedava olmamasının nedeni budur:
Kaç eleman ekleyeceğini kabaca biliyorsan, tekrarlanan yeniden ayırmaları atlamak için önce reserve() çağır. reserve()'in boyutu değil kapasiteyi değiştirdiğine dikkat et - sen elemanları ekleyene kadar vector hâlâ sıfır elemana sahiptir.
Bu yeniden ayırma aynı zamanda en sinsi vector hatasının kaynağıdır. Büyüme depoyu taşıdığı için, vector içine kaydettiğin herhangi bir işaretçi, referans veya iterator, yeniden ayırma yapan bir push_back'ten sonra askıda kalır:
vector<int> v = {1, 2, 3};
int& first = v[0]; // vector içine referans
v.push_back(4); // yeniden ayırabilir...
cout << first; // ASKIDA - serbest bırakılmış belleğe işaret edebilir
Aynısı iterator'lar için de geçerlidir: kaydedilmiş bir iterator ile dönerken push_back ya da erase yapma. Döngü sırasında öğe çıkarman gerekiyorsa, erase'in dönüş değerini ya da std::remove ile erase-remove deyimini kullan.
Sıradaki: map
Bir vector, nesneleri konuma göre aradığında mükemmeldir - eleman 0, eleman 1 vb. Ama çoğu zaman nesneleri bir anahtara göre aramak istersin: bir kullanıcı adı, bir ürün kimliği, bir kelime. İşte std::map bunun içindir. Sırada, C++'ın anahtar-değer konteyneri olan map'i ele alacağız; girişleri nasıl ekleyeceğini, arayacağını ve üzerinde döneceğini ve neredeyse herkesi yanıltan []-varsayılan-oluşturur tuzağını göreceğiz.
Sıkça Sorulan Sorular
C++'ta vector nedir?
std::vector, C++ Standart Kütüphanesi'nden gelen dinamik (yeniden boyutlandırılabilir) bir dizidir. Ham bir diziden farklı olarak kendi boyutunu bilir, push_back ile eleman eklediğinde otomatik olarak büyür ve belleğini senin yerine serbest bırakır. Birini oluşturmak için <vector> ekle ve vector<int> v; yaz.
C++ vector'da [] ile at() arasındaki fark nedir?
v[i] sınır denetimi yapmaz - aralık dışı bir indeks tanımsız davranıştır (çökme veya sessiz bozulma). v.at(i) indeksi denetler ve geçersizse std::out_of_range fırlatır. İndeksi zaten doğruladığın sıkı döngülerde [] kullan; güvenli ve hata ayıklanabilir bir başarısızlık istediğinde at() kullan.
push_back, C++ vector içindeki işaretçileri ve referansları geçersiz kılar mı?
Evet, potansiyel olarak. Bir vector'ın kapasitesi dolduğunda push_back deposunu yeni bir bloğa yeniden ayırır; bu da eski elemanlara işaret eden tüm işaretçileri, referansları ve iterator'ları geçersiz kılar. Bir push_back boyunca bir elemana referans tutma ve sürpriz yeniden ayırmalardan kaçınmak için mümkünse önceden reserve() çağır.