Smart Pointer’lara Giriş
unique_ptr, shared_ptr ve weak_ptr ile güvenli yaşam döngüsü yönetimine giriş.
C++’ta dinamik bellek yönetimi geleneksel olarak new ve delete ile yapılır.
Ancak manuel bellek yönetimi; sızıntılar, çifte serbest bırakma veya asılı pointer (dangling pointer) hatalarına yol açabilir.
Modern C++ (C++11 ve sonrası), bu sorunları çözmek için akıllı pointer (smart pointer) kavramını getirmiştir.
Smart pointer’lar, bellek yönetimini otomatik hale getirir ve hataları büyük oranda ortadan kaldırır.
1. Smart Pointer Nedir?
Smart pointer, bir nesnenin ömrünü yöneten özel bir C++ sınıfıdır. Normal pointer gibi davranır ancak nesnenin yaşam döngüsünü (oluşturma ve yok etme) otomatik kontrol eder. Bu sayede bellek sızıntıları önlenir.
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> ptr = make_unique<int>(42);
cout << *ptr << endl; // 42
}
unique_ptr, shared_ptr ve weak_ptr olmak üzere üç temel smart pointer türü vardır.
Hepsi <memory> başlık dosyasında bulunur.
2. unique_ptr
std::unique_ptr, sahipliği yalnızca bir nesnede bulunan pointer’dır.
Aynı anda yalnızca bir unique_ptr belirli bir nesneye sahip olabilir.
Kopyalanamaz, ancak taşınabilir (std::move() ile).
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> p1 = make_unique<int>(10);
cout << *p1 << endl;
// unique_ptr kopyalanamaz:
// unique_ptr<int> p2 = p1; // HATA!
// Ancak taşınabilir:
unique_ptr<int> p2 = move(p1);
cout << *p2 << endl;
}
Avantajları:
- Otomatik bellek yönetimi
- Performans açısından hafif
- Manuel
deletegerektirmez
unique_ptr kopyalanamaz, bu yüzden paylaşım gereken durumlarda uygun değildir.
3. shared_ptr
std::shared_ptr, bir nesneye birden fazla pointer’ın sahip olmasına izin verir.
Nesnenin ömrü, ona sahip olan pointer sayısına (reference count) bağlıdır.
Son shared_ptr yok edildiğinde nesne otomatik olarak silinir.
#include <iostream>
#include <memory>
using namespace std;
int main() {
shared_ptr<int> p1 = make_shared<int>(100);
shared_ptr<int> p2 = p1; // Paylaşılan sahiplik
cout << "p1: " << *p1 << ", p2: " << *p2 << endl;
cout << "Kullanım sayısı: " << p1.use_count() << endl; // 2
}
Avantajları:
- Bir nesne birden fazla yerde paylaşılabilir
- Son kullanıcı
deleteçağırmak zorunda kalmaz
- Referans sayımı nedeniyle biraz ek bellek ve işlem yükü oluşturur
- Döngüsel referanslarda (circular reference) bellek sızıntısına yol açabilir
4. weak_ptr
std::weak_ptr, shared_ptr tarafından yönetilen bir nesneye “zayıf” (non-owning) referanstır.
weak_ptr nesnenin yaşam süresini uzatmaz, sadece varlığını gözlemlemeye yarar.
Böylece döngüsel referans (circular reference) problemini önler.
#include <iostream>
#include <memory>
using namespace std;
int main() {
shared_ptr<int> sp = make_shared<int>(50);
weak_ptr<int> wp = sp;
cout << "Kullanım sayısı: " << sp.use_count() << endl;
if (auto ptr = wp.lock()) {
cout << "Değer: " << *ptr << endl;
}
}
wp.lock() geçerli bir shared_ptr döndürür;
eğer nesne silindiyse nullptr döner.
Bu yöntem, güvenli erişim sağlar.
5. Döngüsel Referans Problemi (Circular Reference)
shared_ptr’lar birbirini referans ederse, her iki tarafın referans sayısı sıfıra düşmez
ve bellek sızıntısı oluşur.
Bu durum genellikle bağlı (linked) veri yapılarında ortaya çıkar.
#include <memory>
using namespace std;
struct Node {
shared_ptr<Node> next;
// HATA: Döngüsel referans!
};
int main() {
shared_ptr<Node> n1 = make_shared<Node>();
shared_ptr<Node> n2 = make_shared<Node>();
n1->next = n2;
n2->next = n1; // Sonsuz referans döngüsü
}
Bu durumda weak_ptr kullanmak gerekir:
struct Node {
weak_ptr<Node> next; // Artık döngü oluşmaz
};
6. Smart Pointer’larla Fonksiyon Kullanımı
Smart pointer’lar, fonksiyonlara normal pointer gibi parametre olarak geçebilir. Ancak sahiplik modeline dikkat edilmelidir.
void Yazdir(shared_ptr<int> p) {
cout << *p << endl;
}
int main() {
auto ptr = make_shared<int>(77);
Yazdir(ptr); // Güvenli paylaşım
}
Eğer sahiplik devredilmek isteniyorsa, unique_ptr parametresiyle std::move() kullanılmalıdır.
void Al(unique_ptr<int> p) {
cout << *p << endl;
}
int main() {
auto p = make_unique<int>(88);
Al(move(p)); // p artık boş (nullptr)
}
7. Smart Pointer’ların Bellek Yönetimi Avantajları
- Manuel
deletegerektirmez. - Bellek sızıntılarını önler.
- RAII (Resource Acquisition Is Initialization) prensibini uygular.
- Exception durumlarında otomatik bellek temizliği sağlar.
- Döngüsel referanslara karşı
weak_ptrçözüm sunar.
8. Karşılaştırma Tablosu
| Tür | Sahiplik | Kopyalama | Referans Sayımı | Kullanım Alanı |
|---|---|---|---|---|
unique_ptr |
Tek | Yasak | Yok | Tek sahipli nesneler |
shared_ptr |
Paylaşımlı | Serbest | Var | Birden fazla sahipli nesneler |
weak_ptr |
Sahiplik yok | Serbest | Bağımlı | Döngüsel referans önleme |
9. TL;DR
unique_ptr→ tek sahipli, kopyalanamaz, taşınabilir.shared_ptr→ referans sayımıyla paylaşımlı sahiplik.weak_ptr→ paylaşılan nesneyi izler ama sahip olmaz.- Smart pointer’lar bellek sızıntılarını büyük ölçüde önler.
- Döngüsel referanslarda mutlaka
weak_ptrkullanılmalıdır. - Tüm örnekler Visual Studio 2022 veya GCC 11+ derleyicilerinde çalıştırılabilir.