ProgramlamaC++ Backend Geliştirici

C++'da istisna işleme sırasında stack unwinding ile nesnelerin yok edilmesi arasındaki farkları açıklayın. Bir istisna oluştuğunda kaynakların düzgün bir şekilde serbest bırakılmasını nasıl garanti edersiniz?

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap.

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.

Konunun geçmişi

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ı.

Problem

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.

Çözüm

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:

  • Stack unwinding, yığın üzerindeki tüm nesnelerin destructor'larını otomatik olarak çağırır.
  • RAII, istisna meydana gelse bile kaynakların serbest bırakılmasını garanti eder.
  • Catch içinde kaynakları manuel olarak serbest bırakmak nadiren gereklidir: destructor'lar bunu daha iyi yapar.

Kandırmaca soruları.

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!

Yaygın hatalar ve anti-patronlar

  • Destructor'larda kaynakları serbest bırakmayıp yalnızca catch içinde manuel yönetim üzerinde durmak.
  • Destructor'lardan istisna atmak.
  • Heap kaynaklarını yönetmek için akıllı işaretçiler kullanmamak.
  • İç içe geçmiş kaynakları unutmamak (örneğin, bir yapı içinde dosya).

Hayat örneği

Olumsuz durum

Geliştirici, RAII kullanmadan catch blokları aracılığıyla dosyaları manuel olarak kapatır ve belleği serbest bırakır.

Artılar:

  • Kaynakları açıkça yönetir.

Eksiler:

  • Throw'dan catch'e çıkış yapılırken sızıntı kaçınılmaz olacaktır.
  • Kodu sürdürmek ve değiştirmek zordur, serbest bırakmayı unutmak kolaydır.

Olumlu durum

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:

  • Kaynakların güvenilir serbest bırakılması.
  • Daha az kod, bakım kolaylığı.

Eksiler:

  • RAII sarmallarını yazmayı öğrenmek gerekir.
  • Yeni kaynak türleri eklemek yeni sınıflar gerektirir.