STL Genel Bakış
C++ öğrenme yolculuğunda bir noktaya geldin ki artık kendi veri yapılarını sıfırdan yazmak yerine, dünyanın en test edilmiş, en optimize edilmiş kütüphanesini kullanabilirsin. Bu kütüphanenin adı STL — Standard Template Library.
STL'i anlamak, C++ ile profesyonel yazılım geliştirmenin kapısını açar. Bu derste STL'in ne olduğunu, neden bu kadar önemli olduğunu ve üç temel bileşenini tanıyacağız.
STL Nedir?
STL, C++ standart kütüphanesinin en büyük ve en kullanışlı parçasıdır. İçinde hazır veri yapıları (container), bu veri yapıları üzerinde gezinme araçları (iterator) ve veri üzerinde işlem yapan algoritmalar (algorithm) bulunur.
Bir mutfak analojisi düşün: Container'lar senin tencerelerin ve kapların — verini içlerinde tutarsın. Iterator'lar senin ellerın — kapların içine uzanıp elemanlara dokunursun. Algoritmalar ise tarifler — sıralama, arama, dönüştürme gibi işlemleri yaparsın. Bu üç parça birlikte çalışarak her türlü veri işleme ihtiyacını karşılar.
STL'in güzelliği şu: template tabanlı olduğu için herhangi bir veri tipiyle çalışır. int, string, kendi yazdığın sınıf — fark etmez. Aynı vector, aynı sort algoritması hepsinde çalışır.
Neden STL Önemli?
Diyelim ki bir dizi sayıyı sıralamak istiyorsun. STL olmadan kendi sıralama algoritmanı yazman gerekir — bubble sort, merge sort, quicksort... Hepsinde edge case'ler, performans tuzakları var. STL ile tek satır:
std::sort(numbers.begin(), numbers.end());Bu tek satırın arkasında yılların mühendislik tecrübesi, onlarca optimizasyon ve binlerce test var. Kendi yazdığın sıralama algoritmasından neredeyse her zaman daha hızlı çalışır.
STL kullanmanın üç büyük avantajı:
Verimlilik: Her container ve algoritma, zaman ve bellek karmaşıklığı açısından optimize edilmiştir.
Güvenilirlik: Milyonlarca geliştirici tarafından kullanılıp test edilmiştir.
Taşınabilirlik: Standart olduğu için her C++ derleyicisinde çalışır.
Üç Temel Bileşen
STL'in mimarisini anlamak için bu üç bileşeni kafanda netleştirmen gerekiyor. Hepsi birbirinden bağımsız ama birlikte çalışacak şekilde tasarlanmış.
1. Containers (Kapsayıcılar)
Container'lar veriyi depolayan yapılardır. Her birinin farklı bir iç yapısı ve farklı güçlü yönleri var.
#include <vector>
#include <map>
#include <set>
#include <iostream>
int main() {
// Sequence container — sıralı eleman listesi
std::vector<int> numbers = {5, 3, 8, 1, 9};
// Associative container — anahtar-değer çifti
std::map<std::string, int> ages = {
{"Ali", 25},
{"Ayse", 30}
};
// Set — benzersiz elemanlar kümesi
std::set<int> uniqueNumbers = {3, 1, 4, 1, 5};
// Set'te 1 sadece bir kere bulunur
std::cout << "Vector size: " << numbers.size() << "\n";
std::cout << "Ali'nin yasi: " << ages["Ali"] << "\n";
std::cout << "Unique count: " << uniqueNumbers.size() << "\n";
return 0;
}2. Iterators (Yineleyiciler)
Iterator'lar container'ların elemanlarına erişim sağlar. Pointer'lara çok benzerler — aslında pointer'ların genelleştirilmiş halidir.
#include <vector>
#include <iostream>
int main() {
std::vector<int> nums = {10, 20, 30, 40, 50};
// Iterator ile gezinme
for (std::vector<int>::iterator it = nums.begin(); it != nums.end(); ++it) {
std::cout << *it << " ";
}
std::cout << "\n";
// Modern C++ — auto ile çok daha temiz
for (auto it = nums.begin(); it != nums.end(); ++it) {
std::cout << *it << " ";
}
std::cout << "\n";
return 0;
}3. Algorithms (Algoritmalar)
Algoritmalar, container'lar üzerinde işlem yapan fonksiyonlardır. Iterator'lar aracılığıyla çalışırlar — bu sayede herhangi bir container ile kullanılabilirler.
#include <vector>
#include <algorithm>
#include <numeric>
#include <iostream>
int main() {
std::vector<int> nums = {5, 3, 8, 1, 9, 2, 7};
// Sıralama
std::sort(nums.begin(), nums.end());
// Arama
auto it = std::find(nums.begin(), nums.end(), 8);
if (it != nums.end()) {
std::cout << "8 bulundu!\n";
}
// Toplam hesaplama
int total = std::accumulate(nums.begin(), nums.end(), 0);
std::cout << "Toplam: " << total << "\n";
return 0;
}Container Kategorileri
Container'lar üç ana kategoriye ayrılır. Her kategorinin kendine has özellikleri var.
Sequence Containers (Sıralı Kapsayıcılar)
Elemanları ekleme sırasına göre tutarlar. Yani sen hangi sırayla eklediysen, o sırayla kalır.
| Container | Açıklama |
|---|---|
vector | Dinamik dizi — en çok kullanılan |
deque | Çift uçlu kuyruk — baştan ve sondan hızlı ekleme |
list | Çift bağlı liste — ortadan hızlı ekleme/silme |
forward_list | Tek bağlı liste — minimum bellek kullanımı |
array | Sabit boyutlu dizi — boyut derleme zamanında belli |
Associative Containers (İlişkisel Kapsayıcılar)
Elemanları sıralı tutarlar (varsayılan olarak küçükten büyüğe). İç yapıları genellikle kırmızı-siyah ağaçtır (red-black tree).
| Container | Açıklama |
|---|---|
set | Benzersiz elemanlar kümesi |
multiset | Tekrar eden elemanlara izin veren küme |
map | Anahtar-değer çiftleri (benzersiz anahtar) |
multimap | Tekrar eden anahtarlara izin veren map |
Unordered Containers (Sırasız Kapsayıcılar)
Elemanları hash tablosu ile saklarlar. Sıralama garantisi yoktur ama arama/ekleme ortalama O(1) — yani çok hızlıdır.
| Container | Açıklama |
|---|---|
unordered_set | Hash tabanlı benzersiz küme |
unordered_multiset | Hash tabanlı, tekrar izinli küme |
unordered_map | Hash tabanlı anahtar-değer |
unordered_multimap | Hash tabanlı, tekrar izinli map |
#include <set>
#include <unordered_set>
#include <iostream>
int main() {
// Ordered set — elemanlar sıralı tutulur
std::set<int> ordered = {5, 3, 1, 4, 2};
std::cout << "Ordered: ";
for (int x : ordered) std::cout << x << " ";
// Çıktı: 1 2 3 4 5
std::cout << "\n";
// Unordered set — sıra garanti değil ama hızlı
std::unordered_set<int> unordered = {5, 3, 1, 4, 2};
std::cout << "Unordered: ";
for (int x : unordered) std::cout << x << " ";
// Çıktı: sıra belirsiz, örn: 2 4 1 3 5
std::cout << "\n";
return 0;
}Iterator Türleri
Her container farklı yeteneklerde iterator sunar. Iterator türlerini "hareket kabiliyeti" olarak düşün — bazıları sadece ileri gidebilir, bazıları geri de gidebilir, bazıları ise istediğin yere zıplayabilir.
Input Iterator
Sadece okuyor, sadece ileri gidiyor. Bir kere okuduğun elemanı tekrar okuyamazsın. std::istream_iterator buna örnek — dosyadan veya konsoldan okuduğun veriyi bir kere alırsın.
Output Iterator
Sadece yazıyor, sadece ileri gidiyor. std::ostream_iterator buna örnek — çıktı akışına yazar.
Forward Iterator
Okur ve yazar, sadece ileri gider. Ama aynı elemanı birden fazla kez okuyabilirsin. forward_list::iterator buna örnek.
Bidirectional Iterator
İleri ve geri gidebilir. list::iterator, set::iterator, map::iterator bu kategoride.
Random Access Iterator
En güçlü iterator. İleri, geri, ve herhangi bir konuma zıplama yapabilir. vector::iterator ve deque::iterator bu kategoride. Pointer aritmetiği gibi it + 5, it - 3, it[2] gibi işlemler yapabilirsin.
#include <vector>
#include <list>
#include <iostream>
int main() {
// Random access iterator — zıplama yapabilir
std::vector<int> vec = {10, 20, 30, 40, 50};
auto vit = vec.begin();
vit += 3; // 4. elemana zıpla
std::cout << "vec[3] = " << *vit << "\n"; // 40
// Bidirectional iterator — ileri ve geri
std::list<int> lst = {10, 20, 30, 40, 50};
auto lit = lst.end();
--lit; // Son elemana geri git
std::cout << "list son = " << *lit << "\n"; // 50
// lit += 3; // HATA! list iterator random access değil
return 0;
}Iterator yetenek hiyerarşisi:
Input/Output → Forward → Bidirectional → Random Access
(en az) (en çok)Her üst seviye, alt seviyenin tüm yeteneklerine sahiptir. Random access iterator, bidirectional'ın yapabildiği her şeyi yapabilir — artı ekstra.
begin() ve end() Kullanımı
STL'in en temel konsepti range kavramıdır: bir başlangıç noktası ve bir bitiş noktası. begin() ilk elemana, end() ise son elemandan bir sonraki konuma işaret eder.
💡 Neden end() son eleman değil de "bir sonrası"? Bu yarı-açık aralık
[begin, end)tasarımı sayesinde boş container'lar içinbegin() == end()kontrolü yeterli olur. Ayrıca döngülerdeit != end()koşulu temiz ve tutarlı çalışır.
#include <vector>
#include <iostream>
int main() {
std::vector<int> nums = {10, 20, 30};
auto first = nums.begin(); // İlk elemana işaret eder → 10
auto last = nums.end(); // Son elemandan bir sonrasına → ???
// Klasik iterator döngüsü
for (auto it = first; it != last; ++it) {
std::cout << *it << " ";
}
std::cout << "\n";
// Boş container kontrolü
std::vector<int> empty;
if (empty.begin() == empty.end()) {
std::cout << "Container bos!\n";
}
return 0;
}const Iterator'lar
Eğer container'ın elemanlarını değiştirmek istemiyorsan, cbegin() ve cend() kullan. Bu const iterator döndürür — sadece okuma yapabilirsin.
#include <vector>
#include <iostream>
int main() {
std::vector<int> nums = {10, 20, 30};
// cbegin/cend — salt okunur
for (auto it = nums.cbegin(); it != nums.cend(); ++it) {
// *it = 99; // HATA! const iterator ile değiştiremezsin
std::cout << *it << " ";
}
std::cout << "\n";
return 0;
}Reverse Iterator'lar
Sondan başa doğru gezmek istersen rbegin() ve rend() kullan.
#include <vector>
#include <iostream>
int main() {
std::vector<int> nums = {10, 20, 30, 40, 50};
// Tersine gezinme
for (auto it = nums.rbegin(); it != nums.rend(); ++it) {
std::cout << *it << " ";
}
// Çıktı: 50 40 30 20 10
std::cout << "\n";
return 0;
}Range-Based For Döngüsü
Modern C++'ta iterator'ları doğrudan kullanmak yerine çoğu zaman range-based for döngüsü tercih edilir. Arka planda begin() ve end() kullanır ama söz dizimi çok daha temiz.
#include <vector>
#include <map>
#include <iostream>
int main() {
std::vector<int> nums = {10, 20, 30, 40, 50};
// Range-based for — en temiz söz dizimi
for (int n : nums) {
std::cout << n << " ";
}
std::cout << "\n";
// Referans ile — kopyalama olmaz, değiştirme mümkün
for (int& n : nums) {
n *= 2;
}
// const referans — kopyalama yok, değiştirme de yok
for (const int& n : nums) {
std::cout << n << " ";
}
// Çıktı: 20 40 60 80 100
std::cout << "\n";
// Map ile range-based for
std::map<std::string, int> ages = {{"Ali", 25}, {"Veli", 30}};
for (const auto& [name, age] : ages) {
std::cout << name << ": " << age << "\n";
}
return 0;
}⚠️ Range-based for döngüsü içinde container'a eleman ekleme veya silme yapma! Bu, iterator'ları geçersiz kılar (iterator invalidation) ve tanımsız davranışa (undefined behavior) yol açar.
STL Header'ları
Her container ve algoritma kendi header dosyasında bulunur. İhtiyacın olan header'ı #include ile dahil etmelisin.
| Header | İçerik |
|---|---|
<vector> | vector |
<deque> | deque |
<list> | list, forward_list |
<set> | set, multiset |
<map> | map, multimap |
<unordered_set> | unordered_set, unordered_multiset |
<unordered_map> | unordered_map, unordered_multimap |
<algorithm> | sort, find, transform, vb. |
<numeric> | accumulate, iota, vb. |
<iterator> | iterator yardımcıları |
Hepsini Bir Arada Kullanalım
Şimdi üç bileşeni birlikte kullanan küçük bir örnek yapalım:
#include <vector>
#include <algorithm>
#include <iostream>
int main() {
// 1. Container — veriyi tut
std::vector<std::string> names = {
"Zeynep", "Ali", "Merve", "Burak", "Deniz"
};
// 2. Algorithm + Iterator — sırala
std::sort(names.begin(), names.end());
// 3. Gezin ve yazdır
std::cout << "Sirali isimler:\n";
for (const auto& name : names) {
std::cout << " " << name << "\n";
}
// Arama
auto it = std::find(names.begin(), names.end(), "Merve");
if (it != names.end()) {
std::cout << "Merve bulundu, sira: "
<< std::distance(names.begin(), it) << "\n";
}
return 0;
}Bu örnekte:
vectorcontainer olarak veriyi tutuyorsortalgoritması,begin()/end()iterator'ları ile çalışıyorfindalgoritması arama yapıyordistancefonksiyonu iki iterator arası mesafeyi hesaplıyor
Özet
STL (Standard Template Library), C++'ın en güçlü parçasıdır — containers, iterators ve algorithms olmak üzere üç temel bileşenden oluşur.
Container'lar veriyi depolar: sequence (vector, list), associative (set, map), unordered (unordered_map, unordered_set).
Iterator'lar container elemanlarına erişim sağlar — pointer'ların genelleştirilmiş halidir. Input, output, forward, bidirectional ve random access olmak üzere beş türü vardır.
Algoritmalar (sort, find, transform vb.) iterator'lar aracılığıyla herhangi bir container üzerinde çalışır.
begin()ilk elemana,end()son elemandan bir sonrasına işaret eder — bu yarı-açık aralık[begin, end)STL'in temel tasarım ilkesidir.Range-based for döngüsü (
for (auto& x : container)) günlük kullanımda en temiz ve güvenli gezinme yoludur.
AI Asistan
Sorularını yanıtlamaya hazır