Çalışma Zamanında Neden Bellek İstersiniz
Şimdiye kadar oluşturduğunuz her değişken stack üzerinde yaşadı: boyutu derleme zamanında bilinir ve kapsamı sona erdiğinde otomatik olarak yok edilir. Bu hızlı ve güvenlidir, ancak program çalışana kadar ne kadar belleğe ihtiyaç duyacağınızı bilmediğiniz durumu çözemez: kullanıcı girdisine göre boyutlanan bir tampon, kendisini oluşturan fonksiyondan daha uzun yaşaması gereken bir yapı ya da biçimi önceden bilinmeyen bir grafik.
Bu durumlar için C++, new ile heap'ten (free store olarak da bilinir) bellek ayırmanıza ve delete ile geri vermenize izin verir. new ifadesi bir blok ayırır, yapıcıyı çalıştırır ve ona bir işaretçi döndürür; bu, bir önceki sayfada gördüğünüz işaretçilerin üzerine doğrudan inşa edilir.
p değişkeninin kendisi stack üzerinde yaşar; o yalnızca bir işaretçidir. İşaret ettiği int ise heap üzerinde yaşar ve kaç kapsam gelip giderse gitsin, siz delete ile silene kadar canlı kalır.
Stack ile Heap
Bu ayrım, new'in var olma sebebinin ta kendisidir, bu yüzden onu somutlaştırmaya değer.
void demo() {
int a = 10; // stack üzerinde - demo() döndüğünde yok olur
int* b = new int(10); // 'b' stack üzerinde, işaret ettiği int heap üzerinde
} // 'a' yok edildi; heap'teki int SIZAR - asla silinmez
Temel farklar:
- Stack - otomatik ömür, çok hızlı, sınırlı boyut (genellikle birkaç MB), kapsam çıkışında sizin için serbest bırakılır.
- Heap - elle yönetilen ömür, biraz daha yavaş, büyük ve yalnızca
deleteçağırdığınızda serbest bırakılır.
Takas, sorumluluk karşılığında esnekliktir: heap belleği tam olarak istediğiniz süre kadar yaşar, ama onu serbest bırakmayı hatırlaması gereken kişi siz olursunuz.
new[] ile Dizi Ayırma
Uzunluğu çalışma zamanında belirlenen bir bloğa ihtiyaç duyduğunuzda, dizi biçimi olan new T[n]'i kullanın. İlk elemana bir işaretçi döndürür ve onu eşleşen delete[] ile serbest bırakırsınız.
Kural katıdır ve kolayca yanlış yapılır: new'den gelen bellek delete ile, new[]'den gelen bellek ise delete[] ile serbest bırakılır. Bunları karıştırmak (new[] ile ayrılan bir şeyde delete arr kullanmak) makinenizde çalışıyor gibi görünse bile tanımsız davranıştır.
Üç Klasik Hata
Elle bellek yönetiminin, heap hatalarının çoğunu oluşturan küçük bir hata kümesi vardır. Üçünü de tanımayı öğrenin.
1. Bellek sızıntısı - delete'i hiç çağırmazsınız. Blok sonsuza dek ayrılmış kalır. Bir kez zararsız, bir döngü içinde ölümcül.
void leaky() {
int* p = new int(5);
// ... delete yok ...
} // p yok oldu; heap'teki int artık hem erişilemez HEM de serbest bırakılmamış
2. Sallanan işaretçi - belleği serbest bıraktıktan sonra kullanırsınız. İşaretçi hâlâ eski adresi tutar, ama o bellek artık size ait değildir.
3. Çift serbest bırakma - aynı bloğu iki kez delete edersiniz. Bu, heap'in iç kayıtlarını bozar ve genellikle çökmeye yol açar.
int* p = new int(1);
delete p;
delete p; // çift serbest bırakma - tanımsız davranış, çoğu zaman çökme
Bir işaretçiyi sildikten sonra nullptr yapmak hem sallanan kullanımı hem de çift serbest bırakmayı etkisiz hale getirir: nullptr'ın referansını çözmek anında çöker (hata ayıklaması kolay) ve delete nullptr açıkça güvenli bir işlemdir, hiçbir şey yapmaz.
Gerçekçi Bir Ayır-Kullan-Serbest Bırak Döngüsü
Hepsini bir araya getirince, doğru elle yönetimin biçimi şudur: ayır, kullan, tam olarak bir kez serbest bırak ve sonrasında işaretçiye dokunma.
delete u'nun bir sınıf türü için iki şey yaptığına dikkat edin: önce nesnenin yıkıcısını çalıştırır, sonra ham belleği serbest bırakır. Bu sıralama, nesneleriniz kendi kaynaklarına sahip olduğunda önem kazanır.
İnce bir tuzak: new ile delete arasında bir istisna fırlatılırsa, delete hiç çalışmaz ve sızıntı oluşur. Bununla başa çıkmak için her ayırmayı try/catch içine sarmak sıkıcı ve hataya açıktır; bu da tam olarak bir sonraki sayfanın çözdüğü sorundur.
Sırada: Akıllı İşaretçiler
Belleği elle yönetmenin tam maliyetini artık gördünüz: her new, sonradan delete etme sözüdür ve tek bir kaçırılmış, ikilenmiş ya da erken yapılmış serbest bırakma tanımsız davranıştır. Modern C++ bu sözü neredeyse hiç elle vermez. Bir sonraki sayfa akıllı işaretçileri tanıtır - std::unique_ptr ve std::shared_ptr - bir heap ayırmasının sahibi olan ve kapsam dışına çıktıklarında sizin yerinize otomatik olarak delete çağıran nesneler; bu da üç klasik hatayı, derleyicinin ve RAII'nin sizin adınıza hallettiği şeylere dönüştürür.
Sıkça Sorulan Sorular
C++'ta new ile delete arasındaki fark nedir?
new çalışma zamanında heap üzerinde bellek ayırır ve ona bir işaretçi döndürür; delete ise new ile ayrılmış belleği serbest bırakır. Her new tam olarak bir delete ile eşleşmelidir, aksi halde bellek sızdırırsınız. Diziler için delete[] ile birlikte new[] kullanın.
C++'ta delete çağırmayı unutursanız ne olur?
Bir bellek sızıntısı oluşur: heap bloğu artık ona hiçbir şey işaret etmese bile programın tüm ömrü boyunca ayrılmış kalır. Tek bir sızıntı genellikle zararsızdır, ancak bir döngü içindeki ya da uzun süre çalışan bir serviste oluşan sızıntılar, program belleği tükenip çökene kadar büyür.
Modern C++'ta new ve delete'i doğrudan kullanmalı mıyım?
Nadiren. Belleği otomatik olarak serbest bırakan std::vector gibi kapsayıcıları veya akıllı işaretçileri (std::unique_ptr, std::shared_ptr) tercih edin. Akıllı işaretçiler bunları sardığı için ham new/delete'i anlamaya değer, ancak günlük kodda bunlar sızıntı ve sallanan işaretçi kaynağıdır.