C++ Smart Pointer'lar: unique_ptr, shared_ptr ve weak_ptr Rehberi
title: "C++ Smart Pointer'lar: unique_ptr, shared_ptr ve weak_ptr Rehberi" slug: "cpp-smart-pointers-rehberi" category: "cpp" tags: ["cpp", "memory-management", "modern-cpp", "smart-pointers", "best-practices"] excerpt: "C++'ta bellek yönetiminin en kritik silahı olan smart pointer'ları baştan sona öğrenin. unique_ptr, shared_ptr ve weak_ptr'ın ne zaman, nasıl ve neden kullanılacağını gerçek örneklerle keşfedin." published_at: "2026-02-26"
C++ Smart Pointer'lar: unique_ptr, shared_ptr ve weak_ptr Rehberi
Diyelim ki bir arkadaşına kitabını ödünç verdin. İki ihtimal var: ya kitabı geri alırsın ya da arkadaşın "hangi kitap?" der ve kitap kaybolur. C++'ta new ile oluşturduğun her nesne bu kitap gibidir — birisi delete çağırmadığı sürece bellekte asılı kalır. Kimse çağırmazsa: bellek sızıntısı (memory leak). Yanlış zamanda çağırırsa: dangling pointer ve program çöker.
İşte smart pointer'lar tam olarak bu sorunu çözmek için var. C++11 ile gelen unique_ptr, shared_ptr ve weak_ptr, bellek yönetimini otomatikleştiren, hataları derleme zamanında yakalayan ve kodunuzu güvenli hale getiren araçlardır. Modern C++ yazıyorsanız ham pointer (raw pointer) kullanmanız gereken yer neredeyse kalmamıştır.
Bu yazıda üç smart pointer türünü derinlemesine inceleyeceğiz: her birinin sahiplik modeli, dahili çalışma mekanizması, gerçek dünya kullanım alanları ve sizi dertten kurtaracak best practice'ler.
Raw Pointer'ların Sorunu
Smart pointer'ları anlamak için önce raw pointer'ların neden yetersiz kaldığını görmemiz lazım. Aşağıdaki kodu inceleyin:
#include <iostream>
#include <string>
class DatabaseConnection {
public:
DatabaseConnection(const std::string& host) : host_(host) {
std::cout << "Veritabanına bağlanıldı: " << host_ << std::endl;
}
~DatabaseConnection() {
std::cout << "Bağlantı kapatıldı: " << host_ << std::endl;
}
void query(const std::string& sql) {
std::cout << "Sorgu çalıştırılıyor: " << sql << std::endl;
}
private:
std::string host_;
};
void processData() {
DatabaseConnection* conn = new DatabaseConnection("localhost:5432");
conn->query("SELECT * FROM users");
// Burada bir exception fırlarsa ne olur?
// delete conn; asla çağrılmaz → bellek sızıntısı!
if (true) { // Bir koşul
return; // Erken dönüş → delete çağrılmadı!
}
delete conn; // Bu satıra asla ulaşılmaz
}
int main() {
processData();
std::cout << "Program bitti" << std::endl;
return 0;
}Bu kodda üç kritik sorun var:
Erken return: Fonksiyon ortasında
returnçağrıldığındadeletesatırına asla ulaşılmaz.Exception:
query()bir istisna fırlatırsa yinedeleteçağrılmaz.Karmaşık akış: Kod büyüdükçe her yolda
deleteçağırmayı hatırlamak imkansızlaşır.
Smart pointer'lar bu sorunları RAII (Resource Acquisition Is Initialization) prensibiyle çözer. Nesne scope'dan çıktığında (yani yığından — stack'ten — temizlendiğinde) otomatik olarak sahip olduğu kaynağı serbest bırakır. Artık delete yazmayı hatırlamak zorunda değilsiniz.
unique_ptr: Tek Sahiplik, Sıfır Maliyet
unique_ptr, bir kaynağın tek bir sahibi olduğunu garanti eden smart pointer'dır. Sahiplik devredilebilir ama paylaşılamaz. Bir unique_ptr kopyalanamaz, sadece taşınabilir (move).
Bunu şöyle düşünün: evinizin anahtarının tek bir kopyası var. Anahtarı birine verirseniz artık sizde kalmaz.
Temel Kullanım
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class Sensor {
public:
Sensor(const std::string& name, double value)
: name_(name), value_(value) {
std::cout << "Sensor oluşturuldu: " << name_ << std::endl;
}
~Sensor() {
std::cout << "Sensor yok edildi: " << name_ << std::endl;
}
void read() const {
std::cout << name_ << " okuması: " << value_ << std::endl;
}
void setValue(double v) { value_ = v; }
const std::string& getName() const { return name_; }
private:
std::string name_;
double value_;
};
int main() {
// make_unique ile oluştur (C++14, önerilen yol)
auto tempSensor = std::make_unique<Sensor>("Sıcaklık", 23.5);
tempSensor->read(); // Sensor okuması: 23.5
// Sahiplik devri (move semantics)
auto movedSensor = std::move(tempSensor);
// tempSensor artık nullptr!
if (!tempSensor) {
std::cout << "tempSensor artık boş (nullptr)" << std::endl;
}
movedSensor->read(); // Hâlâ çalışır
// unique_ptr'ları vector'de sakla
std::vector<std::unique_ptr<Sensor>> sensors;
sensors.push_back(std::make_unique<Sensor>("Nem", 65.0));
sensors.push_back(std::make_unique<Sensor>("Basınç", 1013.25));
sensors.push_back(std::move(movedSensor)); // Taşıyarak ekle
std::cout << "\nTüm sensorler:" << std::endl;
for (const auto& s : sensors) {
s->read();
}
std::cout << "\nScope sonu — tüm sensorler otomatik silinir:" << std::endl;
return 0;
}Çıktı:
Sensor oluşturuldu: Sıcaklık
Sıcaklık okuması: 23.5
tempSensor artık boş (nullptr)
Sıcaklık okuması: 23.5
Sensor oluşturuldu: Nem
Sensor oluşturuldu: Basınç
Tüm sensorler:
Nem okuması: 65
Basınç okuması: 1013.25
Sıcaklık okuması: 23.5
Scope sonu — tüm sensorler otomatik silinir:
Sensor yok edildi: Nem
Sensor yok edildi: Basınç
Sensor yok edildi: Sıcaklıkunique_ptr'ın Sıfır Maliyeti
unique_ptr'ın en güzel tarafı: çalışma zamanı maliyeti sıfırdır. Derleyici, unique_ptr<T> ile T* arasında aynı makine kodunu üretir. Ekstra bellek tüketmez, ekstra indirection yapmaz. Bu yüzden varsayılan tercihiniz her zaman unique_ptr olmalıdır.
Custom Deleter
Bazen kaynağı serbest bırakma şekliniz standart delete değildir. Dosya tanıtıcıları (file handle), C kütüphanesi kaynakları veya özel havuzlar için custom deleter kullanabilirsiniz:
#include <iostream>
#include <memory>
#include <cstdio>
int main() {
// FILE* için custom deleter
auto fileDeleter = [](FILE* fp) {
if (fp) {
std::cout << "Dosya kapatılıyor..." << std::endl;
fclose(fp);
}
};
{
std::unique_ptr<FILE, decltype(fileDeleter)> file(
fopen("test.txt", "w"), fileDeleter
);
if (file) {
fputs("Smart pointer ile dosya yonetimi!\n", file.get());
std::cout << "Dosyaya yazıldı." << std::endl;
}
} // Scope bitti → fileDeleter otomatik çağrılır
std::cout << "Dosya güvenle kapatıldı." << std::endl;
return 0;
}Bu pattern özellikle C kütüphaneleriyle çalışırken hayat kurtarır. fopen / fclose, malloc / free, veya herhangi bir acquire / release çifti için kullanabilirsiniz.
shared_ptr: Paylaşılan Sahiplik
Bazı senaryolarda bir kaynağa birden fazla yerden erişmeniz gerekir ve hangisinin en son işini bitireceği belli değildir. İşte burada shared_ptr devreye girer.
shared_ptr, bir referans sayacı (reference count) tutar. Her kopya sayacı 1 artırır, her yıkım 1 azaltır. Sayaç sıfıra düştüğünde kaynak otomatik serbest bırakılır. Bunu bir otel odası gibi düşünün: son kişi çıkınca ışıklar kapanır.
Temel Kullanım
#include <iostream>
#include <memory>
#include <string>
#include <vector>
class Logger {
public:
Logger(const std::string& name) : name_(name) {
std::cout << "Logger oluşturuldu: " << name_ << std::endl;
}
~Logger() {
std::cout << "Logger yok edildi: " << name_ << std::endl;
}
void log(const std::string& message) const {
std::cout << "[" << name_ << "] " << message << std::endl;
}
private:
std::string name_;
};
class Service {
public:
Service(const std::string& name, std::shared_ptr<Logger> logger)
: name_(name), logger_(logger) {
logger_->log(name_ + " servisi başlatıldı");
}
~Service() {
logger_->log(name_ + " servisi durduruluyor");
}
void doWork() {
logger_->log(name_ + " çalışıyor...");
}
private:
std::string name_;
std::shared_ptr<Logger> logger_;
};
int main() {
auto logger = std::make_shared<Logger>("AppLogger");
std::cout << "Ref count: " << logger.use_count() << std::endl; // 1
{
auto authService = std::make_shared<Service>("Auth", logger);
std::cout << "Ref count: " << logger.use_count() << std::endl; // 2
auto userService = std::make_shared<Service>("User", logger);
std::cout << "Ref count: " << logger.use_count() << std::endl; // 3
authService->doWork();
userService->doWork();
std::cout << "\nİç scope bitiyor..." << std::endl;
} // authService ve userService yok edilir, ref count 1'e düşer
std::cout << "Ref count: " << logger.use_count() << std::endl; // 1
logger->log("Logger hâlâ hayatta!");
std::cout << "\nMain bitiyor..." << std::endl;
return 0;
}Çıktı:
Logger oluşturuldu: AppLogger
Ref count: 1
[AppLogger] Auth servisi başlatıldı
Ref count: 2
[AppLogger] User servisi başlatıldı
Ref count: 3
[AppLogger] Auth çalışıyor...
[AppLogger] User çalışıyor...
İç scope bitiyor...
[AppLogger] User servisi durduruluyor
[AppLogger] Auth servisi durduruluyor
Ref count: 1
[AppLogger] Logger hâlâ hayatta!
Main bitiyor...
Logger yok edildi: AppLoggershared_ptr'ın Maliyeti
shared_ptr, unique_ptr'ın aksine bedava değildir:
Kontrol bloğu (control block): Referans sayacı ve weak count için heap'te ekstra bir blok ayırır.
make_sharedkullanırsanız nesne ve kontrol bloğu tek bir allocation'da birleştirilir — bu önemli bir optimizasyondur.Atomik operasyonlar: Referans sayacı artırma/azaltma atomiktir (thread-safe). Bu, tek iş parçacıklı kodda bile küçük bir maliyet getirir.
Bellek: Tipik olarak bir
shared_ptr2 pointer boyutundadır (nesne + kontrol bloğu), raw pointer'ın 2 katı.
Bu maliyetler çoğu uygulamada ihmal edilebilir düzeydedir. Ama yüksek performans gereken tight loop'larda farkında olmanız gerekir.
weak_ptr: Döngüsel Referansları Kırma
shared_ptr harika bir araçtır, ama bir tuzağı vardır: döngüsel referans (circular reference). İki nesne birbirini shared_ptr ile tutarsa referans sayaçları asla sıfıra düşmez ve bellek sızıntısı oluşur.
weak_ptr, bir shared_ptr'ın gösterdiği nesneyi sahiplenmeden gözlemleyen bir pointer'dır. Referans sayacını artırmaz. Nesneye erişmek istediğinizde lock() çağırarak geçici bir shared_ptr elde edersiniz — nesne hâlâ hayattaysa.
Döngüsel Referans Problemi ve Çözümü
#include <iostream>
#include <memory>
#include <string>
// YANLIŞ: Döngüsel referans — bellek sızıntısı!
class BadNode {
public:
std::string name;
std::shared_ptr<BadNode> next; // Güçlü referans
BadNode(const std::string& n) : name(n) {
std::cout << "BadNode oluşturuldu: " << name << std::endl;
}
~BadNode() {
std::cout << "BadNode yok edildi: " << name << std::endl;
}
};
// DOĞRU: weak_ptr ile döngü kırılır
class GoodNode {
public:
std::string name;
std::shared_ptr<GoodNode> next;
std::weak_ptr<GoodNode> prev; // Zayıf referans — döngüyü kırar
GoodNode(const std::string& n) : name(n) {
std::cout << "GoodNode oluşturuldu: " << name << std::endl;
}
~GoodNode() {
std::cout << "GoodNode yok edildi: " << name << std::endl;
}
void showPrev() {
// lock() ile güvenli erişim
if (auto p = prev.lock()) {
std::cout << name << "'in önceki düğümü: " << p->name << std::endl;
} else {
std::cout << name << "'in önceki düğümü artık yok!" << std::endl;
}
}
};
int main() {
std::cout << "=== YANLIŞ: Döngüsel referans ===" << std::endl;
{
auto a = std::make_shared<BadNode>("A");
auto b = std::make_shared<BadNode>("B");
a->next = b;
b->next = a; // Döngü! A → B → A → B → ...
std::cout << "Scope bitiyor ama destructor ÇAĞRILMAYACAK!" << std::endl;
}
// BadNode yok edildi mesajı GÖRÜNMEZ — bellek sızıntısı!
std::cout << "\n=== DOĞRU: weak_ptr ile ===" << std::endl;
{
auto first = std::make_shared<GoodNode>("First");
auto second = std::make_shared<GoodNode>("Second");
auto third = std::make_shared<GoodNode>("Third");
// İleri yön: shared_ptr (güçlü sahiplik)
first->next = second;
second->next = third;
// Geri yön: weak_ptr (zayıf gözlem)
second->prev = first;
third->prev = second;
third->showPrev(); // Second
second->showPrev(); // First
std::cout << "Scope bitiyor — tüm düğümler temizlenecek:" << std::endl;
}
return 0;
}Çıktı:
=== YANLIŞ: Döngüsel referans ===
BadNode oluşturuldu: A
BadNode oluşturuldu: B
Scope bitiyor ama destructor ÇAĞRILMAYACAK!
=== DOĞRU: weak_ptr ile ===
GoodNode oluşturuldu: First
GoodNode oluşturuldu: Second
GoodNode oluşturuldu: Third
Third'in önceki düğümü: Second
Second'in önceki düğümü: First
Scope bitiyor — tüm düğümler temizlenecek:
GoodNode yok edildi: First
GoodNode yok edildi: Second
GoodNode yok edildi: ThirdDikkat edin: BadNode destructor'ları asla çağrılmadı. Bu, tipik bir döngüsel referans bellek sızıntısıdır. GoodNode'da ise prev alanında weak_ptr kullandığımız için geri yöndeki referans sayacı artmaz ve zincir doğru şekilde temizlenir.
weak_ptr'ın Diğer Kullanım Alanları
weak_ptr sadece döngüsel referans kırmak için değildir. Bir önbellek (cache) sistemi düşünün: nesneyi önbellekte tutuyorsunuz ama önbelleğin nesneyi hayatta tutmasını istemiyorsunuz. Nesneye ihtiyaç duyan biri varsa (shared_ptr tutan) hayatta kalır, yoksa silinir. lock() ile kontrol edip gerekirse yeniden oluşturabilirsiniz.
Yaygın Hatalar ve Tuzaklar
1. shared_ptr'ı Aynı Raw Pointer'dan İki Kez Oluşturmak
// YANLIŞ — iki ayrı kontrol bloğu, çift delete!
int* raw = new int(42);
std::shared_ptr<int> sp1(raw);
std::shared_ptr<int> sp2(raw); // BÜYÜK HATA!
// sp1 ve sp2 farklı kontrol blokları → ikisi de delete çağırır → CRASHÇözüm: Bir raw pointer'ı asla iki farklı shared_ptr'a vermeyin. make_shared kullanın veya bir shared_ptr'dan kopyalayın.
2. this Pointer'ını shared_ptr'a Sarmak
// YANLIŞ
class Widget {
public:
std::shared_ptr<Widget> getShared() {
return std::shared_ptr<Widget>(this); // TEHLİKE!
}
};Çözüm: std::enable_shared_from_this kullanın:
class Widget : public std::enable_shared_from_this<Widget> {
public:
std::shared_ptr<Widget> getShared() {
return shared_from_this(); // Güvenli
}
};
// Kullanım — nesne shared_ptr ile yönetilmeli
auto w = std::make_shared<Widget>();
auto w2 = w->getShared(); // Aynı kontrol bloğunu paylaşır3. unique_ptr'ı Kopyalamaya Çalışmak
auto ptr = std::make_unique<int>(42);
// auto copy = ptr; // DERLEME HATASI! unique_ptr kopyalanamaz
auto moved = std::move(ptr); // Doğru: sahiplik devri4. make_shared / make_unique Kullanmamak
// Zayıf yol — iki ayrı allocation ve exception-safety riski
std::shared_ptr<Widget> sp(new Widget());
// Güçlü yol — tek allocation, exception-safe
auto sp = std::make_shared<Widget>();
auto up = std::make_unique<Widget>(); // C++14make_shared, nesne ve kontrol bloğunu tek bir bellek tahsisinde birleştirir. Bu hem daha hızlıdır hem de fonksiyon argümanlarında exception safety sağlar.
5. Döngü İçinde Gereksiz shared_ptr Kopyası
void process(const std::vector<std::shared_ptr<Data>>& items) {
for (const auto& item : items) { // const referans — kopya yok, doğru
item->process();
}
// YANLIŞ: her iterasyonda ref count artırılıp azaltılır
for (auto item : items) { // Kopya! Gereksiz atomik işlem
item->process();
}
}Sahipliği paylaşmanız gerekmiyorsa const auto& ile referans alın. Fonksiyon parametrelerinde de aynı kural geçerlidir: nesneyi sadece kullanacaksanız const shared_ptr<T>& veya daha iyisi raw const T& / const T* alın.
Best Practices: Profesyonelin Kuralları
1. Varsayılan Tercihiniz unique_ptr Olsun
Çoğu durumda tek sahiplik yeterlidir. Önce unique_ptr ile başlayın, gerçekten paylaşılan sahiplik gerekirse shared_ptr'a geçin. unique_ptr'dan shared_ptr'a dönüşüm kolaydır:
auto up = std::make_unique<Sensor>("Sıcaklık", 22.0);
std::shared_ptr<Sensor> sp = std::move(up); // SorunsuzTersi mümkün değildir. Yani shared_ptr'dan unique_ptr'a geri dönemezsiniz.
2. Fonksiyon Parametrelerinde Doğru Tip
| Amaç | Parametre Tipi |
|---|---|
| Sadece kullanmak | const T& veya T* |
| Sahipliği devretmek | unique_ptr<T> (by value) |
| Sahipliği paylaşmak | shared_ptr<T> (by value) |
| Sahipliği belki paylaşmak | const shared_ptr<T>& |
3. Diziler İçin unique_ptr
C++14 ve sonrasında unique_ptr dizi desteği sunar:
auto arr = std::make_unique<int[]>(100); // 100 elemanlı int dizisi
arr[0] = 42;
arr[99] = 7;
// Scope bitince delete[] otomatik çağrılırAncak çoğu durumda std::vector veya std::array tercih edilmelidir. Dizi smart pointer'ı daha çok C API'leriyle çalışırken veya sabit boyutlu tamponlar gerektiğinde kullanışlıdır.
4. Thread Safety Hakkında Gerçekler
shared_ptr'ın referans sayacı atomiktir — yani sayaç artırma ve azaltma thread-safe'dir. Ama bu, shared_ptr'ın gösterdiği nesnenin thread-safe olduğu anlamına gelmez! Aynı nesneye birden fazla thread'den yazıyorsanız mutex gibi senkronizasyon mekanizmaları kullanmanız gerekir.
Ayrıca aynı shared_ptr instance'ına (nesneye değil, pointer'ın kendisine) birden fazla thread'den yazma/okuma yapmak da güvenli değildir. Farklı thread'ler kendi shared_ptr kopyalarını tutuyorsa sorun yoktur.
5. Performans Karşılaştırması
| Özellik | Raw Pointer | unique_ptr | shared_ptr |
|---|---|---|---|
| Boyut | 8 byte | 8 byte | 16 byte |
| Heap tahsis | Manuel | Tek | Tek (make_shared) veya İki |
| Kopyalama | Evet | Hayır (taşınır) | Evet (atomik) |
| Çalışma zamanı maliyeti | Yok | Yok | Düşük (atomik sayaç) |
| Thread-safe sayaç | N/A | N/A | Evet |
Gerçek Dünya Örneği: Plugin Sistemi
Birden fazla kavramı birleştiren bir örnek görelim. Bir uygulama için basit bir plugin sistemi tasarlayalım:
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <unordered_map>
// Plugin arayüzü
class IPlugin {
public:
virtual ~IPlugin() = default;
virtual std::string getName() const = 0;
virtual void execute() = 0;
};
// Örnek pluginler
class JsonPlugin : public IPlugin {
public:
std::string getName() const override { return "JsonPlugin"; }
void execute() override {
std::cout << "JSON verisi işleniyor..." << std::endl;
}
};
class CsvPlugin : public IPlugin {
public:
std::string getName() const override { return "CsvPlugin"; }
void execute() override {
std::cout << "CSV verisi dışa aktarılıyor..." << std::endl;
}
};
// Plugin yöneticisi — pluginlerin tek sahibi
class PluginManager {
public:
// Sahipliği devralır (unique_ptr by value)
void registerPlugin(std::unique_ptr<IPlugin> plugin) {
std::cout << "Plugin kaydedildi: " << plugin->getName() << std::endl;
std::string name = plugin->getName();
plugins_[name] = std::move(plugin);
}
// Ham pointer döner — sahiplik devretmez, sadece erişim sağlar
IPlugin* getPlugin(const std::string& name) {
auto it = plugins_.find(name);
if (it != plugins_.end()) {
return it->second.get();
}
return nullptr;
}
void executeAll() {
std::cout << "\nTüm pluginler çalıştırılıyor:" << std::endl;
for (const auto& [name, plugin] : plugins_) {
std::cout << " → ";
plugin->execute();
}
}
size_t count() const { return plugins_.size(); }
private:
std::unordered_map<std::string, std::unique_ptr<IPlugin>> plugins_;
};
// Loglama servisi — birden fazla bileşen tarafından paylaşılır
class LogService {
public:
LogService() { std::cout << "LogService başlatıldı" << std::endl; }
~LogService() { std::cout << "LogService kapatıldı" << std::endl; }
void info(const std::string& msg) {
std::cout << "[INFO] " << msg << std::endl;
}
};
// Uygulama — her şeyi bir araya getirir
class Application {
public:
Application()
: logger_(std::make_shared<LogService>()),
pluginManager_(std::make_unique<PluginManager>()) {
logger_->info("Uygulama başlatılıyor...");
}
void init() {
// Pluginleri oluştur ve sahipliği manager'a devret
pluginManager_->registerPlugin(std::make_unique<JsonPlugin>());
pluginManager_->registerPlugin(std::make_unique<CsvPlugin>());
logger_->info("Toplam " + std::to_string(pluginManager_->count()) + " plugin yüklendi");
}
void run() {
logger_->info("Uygulama çalışıyor");
pluginManager_->executeAll();
// Belirli bir plugin'e erişim (sahiplik almadan)
if (auto* json = pluginManager_->getPlugin("JsonPlugin")) {
logger_->info("JsonPlugin tekrar çalıştırılıyor...");
json->execute();
}
}
// Logger'ı dışarıyla paylaş
std::shared_ptr<LogService> getLogger() const { return logger_; }
private:
std::shared_ptr<LogService> logger_; // Paylaşılan sahiplik
std::unique_ptr<PluginManager> pluginManager_; // Tek sahiplik
};
int main() {
{
Application app;
app.init();
app.run();
// Logger'ı başka bir yerde de kullanabiliriz
auto sharedLogger = app.getLogger();
sharedLogger->info("Main'den loglama");
std::cout << "\nUygulama scope'u bitiyor..." << std::endl;
}
std::cout << "Temizlik tamamlandı." << std::endl;
return 0;
}Bu örnekte üç smart pointer türünün birlikte çalıştığını görüyorsunuz:
`unique_ptr`:
PluginManagerpluginlerin tek sahibidir. Pluginler dışarıdan oluşturulupstd::moveile manager'a devredilir.`shared_ptr`:
LogServicebirden fazla bileşen tarafından paylaşılır.Applicationana sahiptir amagetLogger()ile dışarıya da paylaşabilir.Raw pointer (
get()):getPlugin()sahiplik devretmez, sadece geçici erişim sağlar. Çağıran taraf nesneyi tutmaz.
Ne Zaman Hangisini Kullanmalı?
Karar ağacı basittir:
Sahiplik gerekmiyor mu? → Raw pointer veya referans kullanın.
Tek sahip yeterli mi? →
unique_ptrkullanın. (Vakaların %90'ı buraya düşer.)Birden fazla sahip gerekli mi? →
shared_ptrkullanın.Döngüsel referans veya gözlemci mi? →
weak_ptrkullanın.Emin değil misiniz? →
unique_ptrile başlayın, gerekirse yükseltin.
Sonuç
`unique_ptr` sıfır maliyetle tek sahiplik sağlar — varsayılan tercihiniz budur.
`shared_ptr` referans sayacıyla paylaşılan sahiplik sunar — gerçekten paylaşım gerektiğinde kullanın.
`weak_ptr` döngüsel referansları kırar ve gözlemci pattern'ı sağlar —
shared_ptrile birlikte kullanılır.make_uniquevemake_sharedher zaman tercih edilmelidir — daha güvenli, daha performanslı.Fonksiyon parametrelerinde sahiplik niyetinizi tip ile ifade edin — bu kodu okuyan herkes için bir belgelendirmedir.
Raw
newvedeleteyazmayı bırakın. Modern C++'ta neredeyse hiçbir zaman gerekmez.
Smart pointer'lar, C++'ın en güçlü özelliklerinden biridir. Onları doğru kullanmak, bellek sızıntılarını, dangling pointer'ları ve double-free hatalarını tarihe gömmenizi sağlar. Kodunuz hem daha güvenli hem de niyetinizi daha açık ifade eden bir hale gelir. Her new gördüğünüzde kendinize sorun: "Bu neden bir smart pointer değil?"
Bu yazıyı beğendiniz mi?
Bültene abone olun ve yeni yazılardan ilk siz haberdar olun. Spam yok, söz.
İlgili Yazılar
Spring Boot'ta Custom Annotation Yazma
Spring Boot'ta kendi annotation'ınızı yazın: meta-annotation, AOP ile cross-cutting concerns, validation ve composed ann...
Python'da Context Manager ve with Bloğu Derinlemesine
Python context manager: with bloğu, __enter__/__exit__, contextlib, @contextmanager decorator ve gerçek dünya kaynak yön...
Docker Multi-Stage Build ile Küçük ve Güvenli Image'lar
Docker multi-stage build ile image boyutunu 800MB'den 150MB'ye düşürün. Spring Boot, Node.js ve Go örnekleri ile product...