C++ vector Kullanımı ve Özellikleri
C++ std::vector kullanımını öğrenin. Dinamik diziler, eleman ekleme, silme ve erişim işlemlerini pratik örneklerle keşfedin.
std::vector, C++’ta en yaygın kullanılan sıralı kapsayıcıdır. Ardışıl (contiguous) bellekte dinamik dizi sağlar:
rastgele erişim O(1), sona ekleme amortize O(1), araya ekleme/silme ise eleman kaydırmaları nedeniyle genellikle
O(n) maliyete sahiptir. Bu yazıda kapasite yönetimi (size/capacity), büyüme stratejisi, reserve/resize,
push_back vs emplace_back, iteratör geçerliliği, bellek düzeni (data()), shrink_to_fit ve performans ipuçlarını ele alacağız.
1) Temel Kullanım
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v; // boş
v.push_back(10);
v.push_back(20);
v.push_back(30);
cout << "size=" << v.size() << " capacity=" << v.capacity() << "\n";
cout << v[1] << " " << v.at(2) << "\n"; // [] sınır kontrolsüz, at() kontrollü
for (int x : v) cout << x << " ";
}
size eleman sayısını, capacity ise yeniden ayırma (reallocation) gerekmeksizin saklanabilecek maksimum eleman sayısını gösterir.
2) Contiguous Bellek ve data()
vector elemanları bitişik (contiguous) bellektedir; C API’lerine ham işaretçi ile geçilebilir.
#include <cstring> // memcpy
#include <vector>
int main() {
vector<char> buf(8, 0);
const char* msg = "C++";
memcpy(buf.data(), msg, 3); // güvenli: contiguous depolama
}
Bitişik bellek garantisi, SIMD/IO ve kütüphane entegrasyonları için avantajdır (ör. grafik, ses, ağ paketleri).
3) Büyüme Stratejisi, reserve ve resize
reserve(n): kapasiteyi en azn’e büyütür, boyutu (size) değiştirmez.resize(n): boyutu tam olaraknyapar (kapasite yetmiyorsa büyütür). Yeni elemanları varsayılan değerle ekler.
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v;
v.reserve(1000); // reallocation sayısını düşürür
for (int i=0;i<1000;i++) v.push_back(i);
v.resize(1200, -1); // 200 yeni eleman: -1 ile doldur
cout << v.size() << " " << v.capacity() << "\n";
}
Büyüme faktörü uygulama/derleyiciye göre değişir (tipik olarak ~1.5–2x). Büyük toplu eklemelerde reserve ciddi hız kazandırır.
4) Ekleme ve Silme: push_back vs emplace_back, insert/erase
push_back(x)kopya/taşıma ile ekler;emplace_back(args...)nesneyi yerinde (in-place) kurar.insert(pos, value)araya ekler (geneldeO(n));erase(pos)kaydırma yapar (geneldeO(n)).
#include <string>
#include <vector>
using namespace std;
struct Book { string title; double price; int pages;
Book(string t, double p, int s): title(t), price(p), pages(s) {}
};
int main() {
vector<Book> books;
books.emplace_back("C++ Primer", 180.0, 950); // ctor doğrudan çağrılır
books.push_back(Book{"Effective C++", 150.0, 320}); // geçici + move
books.insert(books.begin(), {"Clean Code", 175.0, 464}); // başa ekleme
books.erase(books.begin()+1); // aradan silme
}
Yerinde kurulum (emplace) özellikle kopyalanması maliyetli tiplerde avantaj sağlar. Araya sık ekleme/silme gerekiyorsa deque veya list düşünün.
5) İteratör Geçerliliği (Iterator Invalidation)
Yeniden ayırma (reallocation) veya eleman kaydırmaları, iteratörleri ve işaretçileri geçersiz kılar.
- Reallocation (kapasite yetmeyip büyüdüğünde): tüm iteratörler, referanslar ve
data()geçersiz olur. - insert/erase: İşlem noktasından sonraki aralıkta iteratörler bozulur; öncesi genellikle korunur.
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> v{1,2,3};
auto it = v.begin();
v.push_back(4); // kapasite yetmezse reallocate olabilir
// it artık geçersiz olabilir; kullanmak UB olur
}
Çözüm: Kritik bölümlerde reserve kullanın; reallocation sonrası iteratörleri yeniden alın.
6) Performans İpuçları
- Önceden bilinen büyüklük için
reservekullanın. - Yere kurulum için
emplace_back,emplacetercih edin. - Kopya yerine taşıma: Tipiniz için move ctor/operator tanımlı olsun (ör.
std::stringzaten taşınabilir). - Sona ekleyin: Orta/baş ekleme O(n) dir; gerekiyorsa başka kapsayıcıyı değerlendirin.
- clear() boyutu 0’a indirir; kapasiteyi düşürmez.
- shrink_to_fit() kapasite azaltma talebidir; non-binding (derleyici uymayabilir).
7) assign, swap, shrink_to_fit
#include <vector>
using namespace std;
int main() {
vector<int> v{1,2,3,4,5};
v.assign(3, 9); // {9,9,9}
vector<int>(v).swap(v); // shrink idiomu: kapasiteyi küçültmek için
v.shrink_to_fit(); // talep (kesin değil)
}
vector<T>(v).swap(v) sık kullanılan “shrink idiomu”dur; geçici ile takas sonrası kapasite genellikle boyuta eşitlenir.
8) Sıralama, Arama ve Algoritmalar
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main() {
vector<int> a{5,1,4,2,3};
sort(a.begin(), a.end()); // 1 2 3 4 5
bool has4 = binary_search(a.begin(), a.end(), 4);
auto pos = lower_bound(a.begin(), a.end(), 3); // ilk >= 3 konum
cout << has4 << " @" << (pos - a.begin()) << "\n";
}
std::ranges (C++20) ile daha okunaklı boru hattı (pipeline) tarzı kullanım mümkündür.
9) İleri Konular: Allocatör, Boolean Vektör, Küçük Nesneler
- Allocator: Özel bellek stratejileri için
vector<T,MyAlloc>yazabilirsiniz (özel havuz, izleme, hizalama). - vector<bool>: Uzmanlaşmış bit-sıkıştırmalı temsil;
bool*yerine proxy döndürür. Bit düzeyinde yer kazandırır ancak işaretçi/iteratör semantiği farklıdır; düşük seviyeli API’lerle dikkatli kullanın. - Küçük nesneler:
vectorkendi başına “small buffer optimization” (SBO) içermez; ancak eleman tipiniz (ör.std::string) içerebilir. Bu, küçük değerlerde heap ayırmayı azaltabilir.
10) Örnek: Kayıt Listesi (Book)
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
struct Book {
string title; double price; int pages;
Book(string t,double p,int s): title(move(t)), price(p), pages(s) {}
};
int main() {
vector<Book> books;
books.reserve(4);
books.emplace_back("C++ Primer", 180.0, 950);
books.emplace_back("Effective C++", 150.0, 320);
books.emplace_back("Clean Code", 175.0, 464);
// Fiyata göre artan sırala
sort(books.begin(), books.end(),
[](const Book& a, const Book& b){ return a.price < b.price; });
// 160+ fiyatlı ilk kitabı bul
auto it = find_if(books.begin(), books.end(),
[](const Book& b){ return b.price >= 160.0; });
if (it != books.end())
cout << it->title << " " << it->price << "\n";
}
11) Sık Yapılan Hatalar
- Araya yoğun ekleme/silme için
vectorkullanmak (performans düşer) →deque/listdeğerlendirin. - Reallocation sonrası eski iteratörleri/referansları kullanmak (tanımsız davranış).
- Gereksiz kopya maliyetleri →
emplace_backve move semantiği kullanın. - Kapasite ihtiyacını öngörmeyip küçük adımlarla büyütmek →
reserveile tahsis sayısını azaltın. vector<bool>’un özel davranışını normalvector<T>gibi varsaymak.
12) TL;DR
vector= contiguous, rastgele erişim O(1), sona ekleme amortize O(1).reservereallocation sayısını azaltır;resizeboyutu değiştirir.emplace_backyerinde kurar; pahalı kopyalardan kaçınır.- Reallocation/insert/erase iteratörleri geçersiz kılabilir.
data()ile C API’lerine köprü kurabilir, IO/SIMD için avantaj sağlayabilirsiniz.shrink_to_fittalep niteliğindedir; “swap ile shrink” daha etkili olabilir.- Araya sık operasyon gerekiyorsa
deque/listdüşünün. - Tüm örnekler Visual Studio 2022 ve GCC 11+ ile uyumludur.