C++'da istisna yönetimi, istisna meydana gelmeden önce çağrı yığınında oluşturulan yerel nesnelerin otomatik olarak yok edilmesi (destructorizasyon) mekanizması olan stack unwinding ile ilişkilidir. Bu fenomen, kaynakların düzgün bir şekilde serbest bırakılması için önemlidir.
C++ tasarlanırken, yerleşik bir çöp toplayıcısı bulunmadığı için, kaynakların (bellek, dosyalar, soketler) serbest bırakılması görevi programcıya düşmektedir. İstisna yönetimi, hata nedeniyle kodun geri dönüşünde dürüst bir serbest bırakımı sağlamalıydı.
Bir istisna sırasında nesnelerin otomatik olarak yok edilmesi mekanizması olmadan, kaynak sızıntısı meydana gelir. Her catch bloğunda kaynakları manuel olarak serbest bırakmazsanız, kod karmaşık ve güvenilmez hale gelir.
C++, bir istisna atıldığında, throw'dan önce yığında yer alan tüm nesnelerin yok edilmesini ters sırada gerçekleştirir. Onların destructor'ları otomatik olarak çağrılır.
Kaynakları sürekli olarak serbest bırakmanın klasik yolu, RAII (Resource Acquisition Is Initialization) modelini kullanmaktır: tüm kaynaklar, yok edildiklerinde kendilerine ait kaynakları serbest bırakan nesnelerin içine "sarıldırılmıştır".
Kod örneği:
#include <iostream> #include <stdexcept> struct FileHandle { FILE* file; FileHandle(const char* path) { file = fopen(path, "r"); if (!file) throw std::runtime_error("Dosya açılamıyor"); } ~FileHandle() { if (file) fclose(file); } }; void processFile(const char* path) { FileHandle fh(path); // Eğer throw olursa geri dönecek // ... dosya ile çalışma throw std::runtime_error("Bir hata"); // fclose otomatik olarak çağrılacak }
Ana özellikler:
Neden sadece try-catch kullanıp catch bloğunda delete veya fclose'ı manuel olarak çağırarak sızıntılardan kaçınamıyoruz?
Cevap:
Bu zahmetli ve güvensizdir: kaynağı kapatmayı unutmak kolaydır, özellikle birden fazla çıkış noktası veya iç içe geçmiş kaynaklar olduğunda. Destructor'lar, istisna "geçiş yaptığında" bile çağrılır, bu nedenle catch'e ihtiyaç yoktur.
Stack unwinding sırasında "yığında" sarılmamış nesneler yok edilirse, heap'deki nesneler yok olacak mı?
Cevap:
Hayır. Stack unwinding, yalnızca yığın üzerinde yer alan nesnelerin destructor'larını çağırır. Heap nesnelerinin doğru bir şekilde yok edilmesi için, bunların yığında (örneğin, akıllı işaretçiler aracılığıyla) bir nesneye sahip olması gerekir.
Eğer stack unwinding sırasında bir destructor kendisi bir istisna atarsa, ne olur?
Cevap:
Eğer stack unwinding sürecinde bir nesne yok edilirken ikinci bir istisna atılırsa, program std::terminate() çağrısı ile sona erecektir. Asla destructor'lardan istisna atmayın!
Geliştirici, RAII kullanmadan catch blokları aracılığıyla dosyaları manuel olarak kapatır ve belleği serbest bırakır.
Artılar:
Eksiler:
Dosyalar ve kaynaklar RAII sarmalı sınıflara sarılır. Sonuç: serbest bırakma, throw/catch'e bakılmaksızın destructor'larda gerçekleşir.
Artılar:
Eksiler: