ProgramlamaC++ Backend Geliştirici

C++'da istisna işlenmesi sırasında stack unwinding ile kaynaklar arasındaki farkı açıklayın. Kaynakların doğru bir şekilde serbest bırakılmasını nasıl garanti edersiniz?

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

Cevap.

Soru hakkında:

C++ başlangıçta performansa odaklanarak tasarlandı, bu nedenle kaynak yönetimi (bellek, dosyalar, akışlar, soketler) genellikle manuel olarak yapılır. Bir istisna durumunda, ele geçirilen kaynakların temizlenmesi gerekir. Stack unwinding (yığın açma), C++'ın bir istisna fırlatma sırasında fonksiyonların düzgün bir şekilde çalışmasını sonlandırmak için kullandığı mekanizmadır.

Problem:

Bir istisna fırlatıldığında kontrol hemen catch bloklarına geçer ve ara fonksiyonlar "açılır": bunların destrüktörleri çağrılır, ancak serbest bırakma fonksiyonlarına açık çağrılar atlanabilir (örneğin, kaynakları otomatik olarak serbest bırakan nesneler kullanılmadığında).

Çözüm:

C++'da kaynak serbest bırakma, serbest bırakma fonksiyonlarını manuel olarak çağırmaktan ziyade destrüktörlere bırakılmalıdır. RAII (Resource Acquisition Is Initialization) şablonu, bir kaynağın serbest bırakılmasını otomatik hale getirmenin kesin bir yoludur. Stack unwinding sırasında, destrüktör çağrılacak ve kaynak, fonksiyondan çıkış yoluna bakılmaksızın serbest bırakılacaktır.

Kod örneği:

#include <fstream> #include <stdexcept> void readFile(const std::string& filename) { std::ifstream file(filename); // İstisna oluşsa bile açılacak ve düzgün bir şekilde kapatılacak if (!file.is_open()) { throw std::runtime_error("Dosya açılamıyor"); } // ... dosyayı okuyoruz } // dosya, istisna olsa bile kapanacak

Anahtar özellikler:

  • Stack unwinding — bir istisna fırlatıldığında nesnelerin yok edilmesi için standart mekanizmadır.
  • Kaynakları her zaman destrüktörlerde serbest bırakın.
  • RAII veya standart sınıflar (örneğin, akıllı göstergeler) kullanın.

Kandırmaca soruları.

Eğer kodda delete ptr; bir catch bloğunda varsa, bu bellek temizliği için yeterli mi?

Hayır, eğer bellek tahsisinden ve catch bloğundan önce bir istisna fırlatılırsa, bellek temizlenmeyebilir. Daha iyi bir seçenek std::unique_ptr kullanmak veya delete ifadesini destrüktörde yazmaktır.

Kod örneği:

void foo() { int* data = new int[10]; // ... throw std::runtime_error("başarısız"); delete[] data; // istisna anında çağrılmayacak }

Stack unwinding, yığında yerleştirilmiş bir nesnenin destrüktörünü atlayabilir mi?

Hayır, tüm yerel nesneler (istisna fırlatma noktasına kadar yok edilmeyen) oluşturma sırasının tersine yok edilecektir ve destrüktörler garanti edilir.

try bloğundan çıkmak için goto veya longjmp kullanabilir miyiz ve destrüktörlerin çağrılmasını bekleyebilir miyiz?

Hayır. C++ yalnızca bir istisna nedeniyle stack unwinding sırasında destrüktörlerin çağrılmasını garanti eder, hatalı akış kontrolünden (goto, setjmp/longjmp gibi) dolayı değil.

Yaygın hatalar ve anti-pattemler

  • Kaynakları try-catch içinde manuel olarak temizlemek, destrüktörleri göz ardı etmek
  • RAII veya standart sınıflar yerine ham göstergeler kullanmak
  • İstisna işleme'yi öyle bir şekilde soyutlamak ki kaynaklar bırakılmasın (örneğin, setjmp/longjmp)

Gerçek yaşam örneği

Olumsuz vaka

Programcı new ile bellek tahsis eder, istisnaları işlerken bellek serbest bırakmayı catch bloğunda unutuyor, ancak fonksiyondan çıkmanın diğer yollarını göz ardı ediyor.

Artıları:

  • İlk başta basit ve "şeffaf" görünüyor

Eksileri:

  • Eğer istisna beklenmedik bir yerde fırlatılırsa, bellek sızıntısı olur
  • Test etmek ve bakımı zor

Olumlu vaka

Tüm kaynaklar için std::unique_ptr ve RAII sınıfları kullanılıyor, serbest bırakma try/catch'ten bağımsızdır.

Artıları:

  • Kaynak sızıntısı yok
  • Hata işlemeyi daha basit hale getirir

Eksileri:

  • Standart kütüphanenin daha iyi bir anlayışını ve dilin sözdizimlerini gerektirir.