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.
Görev
KolayRAII 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.ConnectionPoolsı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 veConnection acquired (X available)yazdır; başarılıysatrue, kullanılabilir bağlantı yoksafalsedöndürrelease(): kullanılabilir bağlantı sayısını artır veConnection released (X available)yazdıravailable(): 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.ConnectionGuardsınıfın RAII desenini temsil eder. Şunları yapmalıdır:- Kurucusunda bir
ConnectionPoolbaş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 connectionyazdırılmalıdır.- Kurucusunda bir
main.cpp: Kapsamlar aracılığıyla RAII'nin otomatik temizleme özelliğini göster.İki girdi oku:
- Havuz kapasitesi (tam sayı)
- İstenecek bağlantı sayısı (tam sayı)
Verilen kapasiteyle bir
ConnectionPoololuştur. Ardından iç içe bir kapsamda (süslü parantezleri kullanarak), istenen sayıdaConnectionGuardnesnesi 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 availableifadesini 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 availableGirdiler 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 availableKoruyucular 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;
}
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
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı11İleri Düzey OOP Kavramları
Kompozisyon ve Kalıtım KarşılaştırmasıCRTP ile Mixin YapılarıPimpl İdiyomuTür Silme (Type Erasure)Enum Sınıfları ve Güçlü TiplendirmeOOP'de İstisna YönetimiÖzel İstisna Hiyerarşileri14Tasarım Kalıpları Bölüm 2
Komut KalıbıAdaptör KalıbıDekoratör KalıbıŞablon Metot KalıbıDurum KalıbıKompozit KalıbıBir Kalıp Olarak RAII3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic KonteynerKendi başına pratik yap: Online C++ derleyicisi