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 (Kaynak Edinimi Başlatmadır), yalnızca bir C++ deyimi değildir; kaynak yönetimini nesne yaşam süresine bağlayan güçlü bir tasarım kalıbıdır. RAII'yi akıllı işaretçilerle daha önce gördünüz, ancak bu kalıp herhangi bir kaynağa uygulanabilir: dosya tanıtıcıları, ağ bağlantıları, mutex'ler veya veritabanı işlemleri.

Temel fikir basittir: kaynakları kurucuda edinin, 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ılır - dosya kapatılır
    
    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 modeli izler - oluşturulurken bir mutex edinir ve yok edilirken serbest bırakır; böylece unutulan unlock işlemlerinden kaynaklanan kilitlenmeleri önler.

RAII sınıflarını uygularken, kaynakların çoğaltılması veya iki kez serbest bırakılması sorunlarını önlemek için kopyalama/taşıma işlemlerini (Beşler Kuralı) ya silmeyi ya da düzgün bir şekilde uygulamayı 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 Bağlantı Havuzu Yöneticisi oluşturalım. Gerçek uygulamalarda veritabanı bağlantıları, uygun şekilde alınması ve serbest bırakılması gereken maliyetli kaynaklardır. İstisnalar oluşsa veya kod yolları karmaşıklaşsa bile bağlantıların her zaman havuza geri döndürülmesini garanti eden bir RAII sarmalayıcısı oluşturacaksın.

Kodunu üç dosya arasında düzenleyeceksin:

  • ConnectionPool.h: Sınırlı sayıda bağlantıyı yöneten basit bir bağlantı havuzu oluştur.

    ConnectionPool sınıfın, kaç bağlantının kullanılabilir olduğunu takip etmelidir (kurucuya geçirilen bir kapasiteyle başla). Şunları uygula:

    • acquire(): bir bağlantı kullanılabiliyorsa sayacı azalt ve Connection acquired (X available) yazdır; başarılıysa true, kullanılabilir bağlantı yoksa false döndür
    • release(): kullanılabilir bağlantı sayısını artır ve Connection released (X available) yazdır
    • available(): mevcut kullanılabilir bağlantı 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:

    • Kurucusunda bir ConnectionPool başvurusu almalı ve bir bağlantı edinmeye çalışmalı
    • Edinme işleminin başarılı olup olmadığını saklamalı
    • Koruyucunun geçerli bir bağlantı tutup tutmadığını kontrol etmek için bir isConnected() yöntemi sağlamalı
    • Yok edicide bağlantıyı otomatik olarak havuza geri bırakmalı (yalnızca bir bağlantı edinilmişse)
    • Kaynak çoğaltılmasını önlemek için kopya kurucusunu ve kopya atamasını silmeli (Beş Kuralı değerlendirmesi)

    Yok edici çalıştığında, bir bağlantı tutuluyorsa havuzda release çağrılmadan önce Guard releasing connection yazdırılmalıdır.

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

    İki girdi oku:

    1. Havuz kapasitesi (tam sayı)
    2. İstenecek bağlantı sayısı (tam sayı)

    Verilen kapasiteyle bir ConnectionPool oluştur. Ardından iç içe bir kapsamda (süslü parantezleri kullanarak), istenen sayıda ConnectionGuard nesnesi oluştur ve bunları bir vektörde sakla. Her koruyucu 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 değeri 1'den başlar)

    Kapsam sona erdikten sonra (koruyucular yok edildikten sonra), havuzun 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

Koruyucular kapsam dışına çıktığında bağlantıların nasıl otomatik olarak serbest bırakıldığına dikkat et. Ana kodunda release'i hiçbir zaman açıkça çağırmazsın. Yok ediciler oluşturulma sırasının tersi sırada çalışır (son koruyucu ilk olarak yok edilir) ve edinilen her bağlantının geri döndürülmesi garanti edilir. RAII'nin gücü budur: kaynak temizleme, kapsamın nasıl sona erdiğinden bağımsız olarak otomatik ve güvenilir ş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 kapasiteyle 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 adet 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