Menu
Coddy logo textTech

Bir Kalıp Olarak RAII

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

RAII (Resource Acquisition Is Initialization), yalnızca bir C++ deyimi değildir; kaynak yönetimini nesnenin yaşam süresine bağlayan güçlü bir tasarım kalıbıdır. RAII'yi akıllı işaretçilerle zaten gördün, ancak bu kalıp her türlü kaynak için geçerlidir: dosya tanıtıcıları, ağ connections, mutex'ler veya veritabanı işlemleri.

Temel fikir basittir: kaynakları Constructor'da acquire edin, kaynakları yıkıcıda serbest bırakın. C++ nesneler kapsam dışına çıktığında yıkıcıların çalışmasını garanti ettiğinden, temizleme işlemi otomatik olarak gerçekleşir - istisnalar oluştuğunda bile:

#include <iostream>
#include <fstream>

class FileGuard {
    std::ofstream file;
public:
    FileGuard(const std::string& filename) : file(filename) {
        if (!file.is_open()) {
            std::cout << "Failed to open file\n";
        }
    }
    
    void write(const std::string& text) {
        if (file.is_open()) file << text;
    }
    
    ~FileGuard() {
        if (file.is_open()) {
            file.close();
            std::cout << "File closed automatically\n";
        }
    }
};

int main() {
    {
        FileGuard guard("output.txt");
        guard.write("Hello RAII");
    }  // Destructor burada çağrıldı - dosya kapatıldı
    
    std::cout << "After scope\n";
}

RAII, çok iş parçacıklı kodda kilitleri yönetirken öne çıkar. Standart kitaplığın std::lock_guard sınıfı bu deseni izler: oluşturulurken bir mutex acquire eder ve yok edilirken serbest bırakır; böylece unutulan kilit açma işlemlerinden kaynaklanan deadlock'ları önler.

RAII sınıflarını uygularken, kaynakların çoğaltılması veya iki kez serbest bırakılması sorunlarını önlemek için either delete etmeyi ya da copy/move işlemlerini doğru şekilde uygulamayı (Rule of Five) unutma. RAII, hataya açık manuel kaynak yönetimini güvenli ve otomatik temizlemeye dönüştürür.

challenge icon

Görev

Kolay

RAII kullanarak veritabanı bağlantılarını güvenli bir şekilde yönetmek için bir Connection Pool Manager oluşturalım. Gerçek uygulamalarda veritabanı bağlantıları, düzgün şekilde acquire edilmesi ve release edilmesi gereken pahalı kaynaklardır. İstisnalar oluşsa veya kod yolları karmaşık hâle gelse bile bağlantıların pool'a her zaman geri döndürülmesini garanti eden bir RAII sarmalayıcısı oluşturacaksın.

Kodunu üç dosya arasında organize edeceksin:

  • ConnectionPool.h: Sınırlı sayıda bağlantıyı yöneten basit bir connection pool Create et.

    ConnectionPool sınıfın kaç bağlantının available olduğunu izlemelidir (Constructor'a geçirilen bir capacity ile başla). Şunları uygula:

    • acquire() — bir bağlantı available ise sayıyı azalt ve Connection acquired (X available) yazdır; başarılıysa true, hiç bağlantı available değilse false döndür
    • release() — available sayısını artır ve Connection released (X available) yazdır
    • available() — mevcut available bağlantıların sayısını döndürür
  • ConnectionGuard.h: Tek bir bağlantıyı güvenli bir şekilde yöneten RAII sarmalayıcısını oluştur.

    ConnectionGuard sınıfın RAII desenini temsil eder. Şunları yapmalıdır:

    • Constructor'ında bir ConnectionPool referansı almalı ve bir bağlantı acquire etmeye çalışmalı
    • acquisition işleminin başarılı olup olmadığını saklamalı
    • Guard'ın geçerli bir bağlantı tutup tutmadığını kontrol etmek için bir isConnected() metodu sağlamalı
    • Bağlantıyı (yalnızca biri acquire edildiyse) Destructor'da otomatik olarak pool'a release etmeli
    • Kaynak çoğaltılmasını önlemek için copy constructor ve copy assignment'ı Delete etmeli (Rule of Five değerlendirmesi)

    Destructor çalıştığında, bir bağlantı tutuluyorsa pool üzerinde release çağrılmadan önce Guard releasing connection yazdırılmalıdır.

  • main.cpp: Scope'lar aracılığıyla RAII'nin otomatik temizleme özelliğini göster.

    İki girdi oku:

    1. Pool capacity (integer)
    2. İstenen bağlantı sayısı (integer)

    Verilen capacity ile bir ConnectionPool Create et. Ardından iç içe bir scope içinde (curly braces kullanarak), istenen sayıda ConnectionGuard nesnesi oluştur ve bunları bir vector'de sakla. Her guard için bağlantının başarılı olup olmadığını yazdır:

    • Bağlandıysa: Guard N: Connected
    • Bağlanmadıysa: Guard N: Failed to connect

    (burada N, 1'den başlar)

    Scope sona erdikten sonra (guard'lar destroyed olduğunda), pool'un son durumunu gösteren After scope: X connections available ifadesini yazdır.

Örneğin, girdiler 2 ve 3 olduğunda:

Connection acquired (1 available)
Guard 1: Connected
Connection acquired (0 available)
Guard 2: Connected
Guard 3: Failed to connect
Guard releasing connection
Connection released (1 available)
Guard releasing connection
Connection released (2 available)
After scope: 2 connections available

Girdiler 3 ve 2 olduğunda:

Connection acquired (2 available)
Guard 1: Connected
Connection acquired (1 available)
Guard 2: Connected
Guard releasing connection
Connection released (2 available)
Guard releasing connection
Connection released (3 available)
After scope: 3 connections available

Guard'lar scope dışına çıktığında bağlantıların nasıl otomatik olarak release edildiğine dikkat et—main kodunda hiçbir zaman açıkça release çağırmazsın. Destructor'lar oluşturulma sırasının tersine çalışır (son guard ilk önce destroyed olur) ve acquire edilmiş her bağlantının geri döndürülmesi garanti edilir. RAII'nin gücü budur: kaynak temizliği, scope nasıl sona ererse ersin otomatik ve güvenilir bir şekilde gerçekleşir.

Kendin dene

#include <iostream>
#include <vector>
#include "ConnectionPool.h"
#include "ConnectionGuard.h"

using namespace std;

int main() {
    // Girdileri oku
    int capacity;
    int numConnections;
    cin >> capacity;
    cin >> numConnections;
    
    // TODO: Verilen kapasite ile bir ConnectionPool oluştur
    
    // TODO: Süslü parantezler kullanarak iç içe bir kapsam oluştur
    {
        // TODO: ConnectionGuard nesnelerini saklamak için bir vector oluştur
        // İpucu: ConnectionGuard için işaretçiler veya akıllı işaretçiler kullanmanız gerekecek çünkü
        // kopya oluşturucusu silinmiş
        
        // TODO: numConnections kadar guard oluşturmak için döngü
        // Her guard için şunlardan birini yazdır:
        // "Guard N: Connected" or "Guard N: Failed to connect"
        // burada N 1'den başlar
        
    }
    // Kapsam bittiğinde guard'lar burada yok edilir
    
    // TODO: Print "After scope: X connections available"
    
    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

Kendi başına pratik yap: Online C++ derleyicisi