← Kursa Dön
📄 Text · 15 min

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.

ContainerAçıklama
vectorDinamik 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_listTek bağlı liste — minimum bellek kullanımı
arraySabit 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).

ContainerAçıklama
setBenzersiz elemanlar kümesi
multisetTekrar eden elemanlara izin veren küme
mapAnahtar-değer çiftleri (benzersiz anahtar)
multimapTekrar 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.

ContainerAçıklama
unordered_setHash tabanlı benzersiz küme
unordered_multisetHash tabanlı, tekrar izinli küme
unordered_mapHash tabanlı anahtar-değer
unordered_multimapHash 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çin begin() == end() kontrolü yeterli olur. Ayrıca döngülerde it != 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:

  • vector container olarak veriyi tutuyor

  • sort algoritması, begin()/end() iterator'ları ile çalışıyor

  • find algoritması arama yapıyor

  • distance fonksiyonu 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.