Menu
Coddy logo textTech

Derinlemesine Lambda İfadeleri

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 85 / 104.

C++11 ile tanıtılan Lambda ifadeleri, satır içinde (inline) tanımlayabileceğiniz anonim fonksiyonlardır. STL algoritmalarıyla temel lambdaları görmüş olsanız da, tam sözdizimlerini anlamak; değişkenleri yakalamak ve onlara nasıl erişildiğini kontrol etmek için güçlü yeteneklerin kapısını açar.

Tam lambda sözdizimi şöyledir: [capture](parameters) mutable -> return_type { body }. Yakalama ifadesi (capture clause), lambdanın hangi dış değişkenlere erişebileceğini ve nasıl erişeceğini belirler:

#include <iostream>

int main() {
    int x = 10;
    int y = 20;
    
    auto byValue = [x]() { return x * 2; };        // x'in kopyası
    auto byRef = [&y]() { y += 5; };               // y'ye referans
    auto allByValue = [=]() { return x + y; };     // Hepsini kopyala
    auto allByRef = [&]() { x++; y++; };           // Hepsine referans
    auto mixed = [x, &y]() { y += x; };            // İkisini karıştır
    
    byRef();
    std::cout << y << "\n";  // 25
}

Varsayılan olarak, değer ile yakalanan (captured-by-value) değişkenler lambda içinde const'tur. mutable anahtar kelimesi bu kopyaların değiştirilmesine izin verir:

int counter = 0;
auto increment = [counter]() mutable {
    return ++counter;  // Lambda'nın kopyasını değiştirir
};

std::cout << increment() << "\n";  // 1
std::cout << increment() << "\n";  // 2
std::cout << counter << "\n";      // 0 - orijinal değişmedi

C++14, yeni değişkenler oluşturmanıza veya nesneleri lambda içine taşımanıza olanak tanıyan başlatma yakalamalarını (init captures) ekledi:

auto ptr = std::make_unique<int>(42);
auto lambda = [p = std::move(ptr)]() {
    return *p;
};  // Sahiplik lambda içine aktarıldı

Lambdalar; geri çağırmalar (callbacks), özel karşılaştırıcılar veya olay işleyiciler için davranışı bir parametre olarak aktarmanız gerektiğinde, ayrı fonksiyon nesneleri tanımlamanıza gerek kalmadan OOP'de özellikle yararlıdır.

challenge icon

Görev

Kolay

Farklı yakalama modlarına sahip lambda ifadelerinin gücünü gösteren bir olay işleyici (event handler) sistemi kuralım. Geri çağırmaları (callbacks) depolayan ve çağıran basit bir olay dağıtıcı (event dispatcher) oluşturacak ve lambdaların harici durumları çeşitli şekillerde nasıl yakalayabildiğini göstereceksiniz.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • EventDispatcher.h: Olay geri çağırmalarını yöneten bir EventDispatcher sınıfı tanımlayın.

    Dağıtıcınız, geri çağırmaları std::function<void()> türünde bir std::vector kullanarak saklamalıdır. Şu metotları ekleyin:

    • addCallback(std::function<void()> callback) — listeye bir geri çağırma ekler
    • fireAll() — saklanan tüm geri çağırmaları sırayla çağırır
    • clear() — tüm geri çağırmaları kaldırır

    <functional> ve <vector> kütüphanelerini dahil etmeniz gerekecektir.

  • EventDispatcher.cpp: Dağıtıcınız için metotları uygulayın. fireAll() metodu basitçe tüm geri çağırmalar üzerinde döngü kurmalı ve her birini çağırmalıdır.
  • main.cpp: İki girdi okuyun:
    1. Bir taban sayı (tam sayı)
    2. Bir çarpan (tam sayı)

    Bir EventDispatcher oluşturun ve üç geri çağırma ekleyerek farklı lambda yakalama tekniklerini gösterin:

    1. Taban sayıyı değer ile (by value) yakalayan ve şunu yazdıran bir lambda: Base value: [base]
    2. Çarpanı referans ile (by reference) yakalayan, değerini 1 artıran ve ardından şunu yazdıran bir lambda: Multiplier after increment: [multiplier]
    3. 0 ile başlatılmış bir sayaç (counter) değişkenini değer ile yakalayan, her çağrıldığında onu artıran ve şunu yazdıran bir mutable lambda: Call count: [counter]

    Tüm geri çağırmaları ekledikten sonra, farklı yakalama modlarının birden fazla çağrı boyunca nasıl davrandığını görmek için fireAll() metodunu iki kez çağırın. İki fireAll() çağrısı arasında ayırıcı olarak --- yazdırın.

    Son olarak, her iki turdan sonra, referans yakalamanın onu nasıl etkilediğini göstermek için main içindeki çarpan değişkeninin son değerini yazdırın: Final multiplier: [multiplier]

Örneğin, 10 ve 5 girdileriyle:

Base value: 10
Multiplier after increment: 6
Call count: 1
---
Base value: 10
Multiplier after increment: 7
Call count: 1
Final multiplier: 7

42 ve 0 girdileriyle:

Base value: 42
Multiplier after increment: 1
Call count: 1
---
Base value: 42
Multiplier after increment: 2
Call count: 1
Final multiplier: 2

Temel davranışlara dikkat edin: değer ile yakalama orijinal taban sayıyı değiştirmez, referans ile yakalama main içindeki gerçek çarpan değişkenini değiştirir (çağrılar boyunca birikir) ve mutable lambdanın sayacı her turda 1 değerini okur. Bunun nedeni fireAll() metodunun geri çağırmaları değer ile döndürmesidir: her tur, saklanan std::function'ın yeni bir kopyasını çağırır; bu kopyanın yakalanan sayacı 0'dan başlar ve 1'e artırılır, bu nedenle saklanan geri çağırmanın kendi durumu asla ilerlemez.

Kendin dene

#include <iostream>
#include "EventDispatcher.h"

using namespace std;

int main() {
    int base;
    int multiplier;
    cin >> base;
    cin >> multiplier;
    
    EventDispatcher dispatcher;
    
    // TODO: base değişkenini DEĞER İLE yakalayan bir lambda ekleyin
    // Şunu yazdırmalı: "Base value: [base]"
    
    // TODO: multiplier değişkenini REFERANS İLE yakalayan bir lambda ekleyin
    // multiplier değerini 1 artırmalı, ardından şunu yazdırmalı: "Multiplier after increment: [multiplier]"
    
    // TODO: Bir sayacı (0 ile başlatılmış) değer ile yakalayan MUTABLE bir lambda ekleyin
    // Sayacı artırmalı ve şunu yazdırmalı: "Call count: [counter]"
    
    // TODO: Tüm geri çağırmaları (callbacks) tetiklemek için fireAll() fonksiyonunu çağırın
    
    // TODO: Ayırıcı olarak "---" yazdırın
    
    // TODO: fireAll() fonksiyonunu tekrar çağırın
    
    // TODO: multiplier değişkeninin son değerini yazdırın: "Final multiplier: [multiplier]"
    
    return 0;
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler