Yığın ve heap farkı, new/delete tuzakları, RAII ve unique_ptr ile shared_ptr ile güvenli bellek yönetimi.
C++'ı güçlü yapan şey belleği elle yönetebilmendir; tehlikeli yapan da aynı şeydir. Bu derste belleğin iki ana bölgesini, elle yönetimin tuzaklarını ve modern C++'ın bu işi nasıl güvenli hale getirdiğini göreceğiz.
Yığın (stack) ve heap
- Yığın (stack): Yerel değişkenlerin yaşadığı bölge. Hızlıdır ve fonksiyon bitince değişkenler otomatik silinir. Boyutu sınırlıdır.
- Heap:
newile istediğin kadar bellek ayırdığın bölge. Büyüktür ama ayırdığını sen geri vermelisin.
new ve delete
int* p = new int(42); // heap'te bir int olustur
std::cout << *p << '\n';
delete p; // geri ver
p = nullptr; // artik gecersiz olan adresi temizle
double* dizi = new double[100]; // 100 elemanlik dizi
delete[] dizi; // dizi icin delete[] sart
Sorun şu: delete yazmayı unutursan bellek hiç geri verilmez (bellek sızıntısı). İki kez delete edersen program çöker. delete ettikten sonra işaretçiyi kullanırsan tanımsız davranış olur. Dallanan koddan erken çıkarsan (örneğin return ya da istisna) delete satırına hiç ulaşılmaz. Elle yönetim bu yüzden hata yapmaya çok açıktır.
RAII: kaynağı nesneye bağla
C++'ın çözümü RAII (Resource Acquisition Is Initialization) fikridir: kaynağı bir nesnenin yapıcısında al, yıkıcısında (destructor) geri ver. Nesne kapsamdan çıkınca yıkıcı otomatik çalışır. Böylece unutmak imkansız hale gelir. Bu fikir yalnızca bellek için değil; dosyalar, kilitler, seri port bağlantıları için de geçerlidir.
Standart kütüphane bu iş için akıllı işaretçiler sunar. Başlık: #include <memory>.
#include <iostream>
#include <memory>
class Sensor {
public:
explicit Sensor(int id) : id_(id) {
std::cout << "Sensor " << id_ << " acildi\n";
}
~Sensor() {
std::cout << "Sensor " << id_ << " kapandi\n";
}
void oku() const {
std::cout << "Sensor " << id_ << " okuyor\n";
}
private:
int id_;
};
int main() {
Sensor* ham = new Sensor(1);
ham->oku();
delete ham; // elle: unutursan sizinti
{
auto tekil = std::make_unique<Sensor>(2);
tekil->oku();
} // blok bitti: otomatik silinir
auto a = std::make_shared<Sensor>(3);
{
auto b = a; // sahiplik paylasildi
std::cout << "sayac: " << a.use_count() << '\n';
}
std::cout << "sayac: " << a.use_count() << '\n';
std::cout << "main bitiyor\n";
return 0;
}
Sensor 1 acildi
Sensor 1 okuyor
Sensor 1 kapandi
Sensor 2 acildi
Sensor 2 okuyor
Sensor 2 kapandi
Sensor 3 acildi
sayac: 2
sayac: 1
main bitiyor
Sensor 3 kapandi
unique_ptr ve shared_ptr
std::unique_ptr
Nesnenin tek sahibi vardır. Kopyalanamaz, yalnızca std::move ile devredilir. Sahip kapsamdan çıkınca nesne silinir. Neredeyse her durumda varsayılan seçim budur ve ham işaretçiye göre ek maliyeti yok denecek kadar azdır. Üretmek için std::make_unique<Tip>(argümanlar) kullan.
std::shared_ptr
Birden çok sahip olabilir. Bir sayaç kaç sahip olduğunu tutar; sayaç sıfıra inince nesne silinir. Örnekte b = a satırı sayacı 2 yaptı, iç blok bitince 1'e düştü ve Sensor 3 ancak main bitince kapandı. Sayaç yönetimi küçük bir maliyet getirdiği için ihtiyaç yoksa unique_ptr tercih edilir.
Kural
Modern C++'ta kod yazarken çıplak new ve delete kullanmamayı hedefle. Dizi gerekiyorsa std::vector, tek nesne gerekiyorsa make_unique. Ham new yalnızca düşük seviyeli kütüphane kodunda görülmelidir.
Gömülü sistem notu: Birçok mikrodenetleyici projesinde dinamik bellek tamamen yasaktır (new, malloc, hatta std::vector büyümesi). Sebep: uzun süre çalışan cihazlarda heap parçalanır ve bir gün ayırma başarısız olur; bu da sahada cihazın donması demektir. Bu tür sistemlerde bellek derleme anında ayrılır: statik/global nesneler ya da std::array. Kuralları öğrenmek önemli; ama hedef platformun kurallarına uy.
Bellek hatalarını bulmak için araçlar vardır: g++ ile -fsanitize=address bayrağını eklersen sızıntılar ve sınır aşımları çalışma anında raporlanır. Valgrind (Linux) benzer iş görür. Şüpheli bir programda ilk denenecek şey budur.
Kendin dene
- Örnekteki
Sensorsınıfıyla birstd::vector<std::unique_ptr<Sensor>>oluştur; üç sensörüpush_back(std::make_unique<Sensor>(i))ile ekle ve çıkışta yıkıcıların çalıştığını gözle. delete ham;satırını yorum yap ve sızıntıyı-fsanitize=addressile yakala.unique_ptr'ı başka bir değişkene kopyalamaya çalış; derleyicinin hatasını oku,std::moveile düzelt.
// Mini sınav
Bir seçeneğe tıkla; doğru mu yanlış mı, anında ve nedeniyle birlikte göreceksin.
Bitirdiğinde işaretle: ilerlemen kaydolur, +20 XP kazanırsın.
İpucu: ← → tuşlarıyla dersler arasında gezinebilirsin.