---
title: 'Akıllı İşaretçiler'
source: 'https://academia.sh/tr/kurslar/cpp-standart-kutuphane/akilli-isaretciler'
course: 'Kaynak Yönetimi ve Standart Kütüphane'
language: tr
updated: '2026-09-26T03:02:47+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'unique_ptr, shared_ptr ve weak_ptr arasındaki sahiplik farkını ayırt eder'
    - 'bir taşıma işleminin kaynakta bıraktığı durumu standardın verdiği garantiyle açıklar'
    - 'std::move çağrısının tek başına hiçbir şey taşımadığını gösteren bir örnek kurar'
    - 'döngüsel referansın sızıntısına karşı weak_ptr ile tanımlı bir karşılık yazar'
---

# Akıllı İşaretçiler

Bir unique_ptr, shared_ptr ya da weak_ptr üzerinde çalışan bir işlem bittiğinde kaynakta ne kaldığını dört sınıfla adlandırma: aynen, değişti, boş, belirsiz.

Önceki kurs bir ifadenin hangi nesneyle çalıştığını ölçtü ve kopyalamanın tek yol olmadığını hatırlatarak kapandı: bir işlem, hedefine yeni bir kopya açmak yerine kaynağın elindekini devralabilir. Bu devir işlemine taşıma denir. Taşıma bittiğinde kaynak nesne yok olmaz — yıkıcısı yine çalışacaktır — ama artık elinde neyin kaldığı işlemin türüne göre değişir. Bu kurs, bir işlemden sonra kaynakta ne kaldığını dört sınıfla adlandırıyor; bu ders o dört sınıfı akıllı işaretçiler (smart pointer) üzerinden tanıtıyor.

## Dört Sınıf ve Çekirdeğin Tam Koşumu

Aşağıdaki üç dosya, bu kursun on bir dersinin ortak ölçüm aracını kurar. `kalan.hpp` dört sınıfı adlandırır:

```cpp
// kalan.hpp — islemden sonra kaynakta ne kaldigini adlandiran cekirdek
#ifndef KALAN_HPP
#define KALAN_HPP
#include <string>
enum class Kalan { aynen, degisti, bos, belirsiz };
// Once/sonra gozlemini sinifa cevirir. Bosalma esitlikten once denetlenir.
Kalan kiyasla(bool ayni_kaldi, bool bosaldi);
// Islem adini ve sinifi tek satirda basar, sinif sayacini artirir.
void raporla(const std::string& islem, Kalan k);
// Belirsiz kaynaga bakilmaz: once yeni deger verilir ya da kaynak yok edilir.
template <typename YeniDeger>
void belirsiz(const std::string& islem, YeniDeger yeni_deger) {
    yeni_deger();
    raporla(islem, Kalan::belirsiz);
}
// Sinif sayilarini ve ust ayrimi basar.
void ozet();
#endif
```

`kalan.cpp` bu dört sınıfı satır biçimine ve bir sayaca bağlar:

```cpp
// kalan.cpp — siniflama, satir bicimi ve sayac
#include "kalan.hpp"
#include <cstdio>
namespace {
int sayac[4] = {0, 0, 0, 0};
const char* const adlar[4] = {"aynen", "degisti", "bos", "belirsiz"};
}
Kalan kiyasla(bool ayni_kaldi, bool bosaldi) {
    if (bosaldi) return Kalan::bos;
    return ayni_kaldi ? Kalan::aynen : Kalan::degisti;
}
void raporla(const std::string& islem, Kalan k) {
    int i = static_cast<int>(k);
    ++sayac[i];
    std::printf("%-32s %s\n", islem.c_str(), adlar[i]);
}
void ozet() {
    std::printf("siniflar: aynen %d, degisti %d, bos %d, belirsiz %d\n",
                sayac[0], sayac[1], sayac[2], sayac[3]);
    std::printf("durumu soylenen %d, soylenmeyen %d\n",
                sayac[0] + sayac[1] + sayac[2], sayac[3]);
}
```

Kalan bölüm on bir işlemi ölçer ve bu dersin kendi akıllı işaretçi örneklerini ekler:

```cpp
// kaynagin-durumu.cpp — cekirdegin tam kosumu ve akilli isaretci derinlestirmesi
#include <algorithm>
#include <map>
#include <memory>
#include <ranges>
#include <string>
#include <utility>
#include <vector>
#include "kalan.hpp"
static void akilli() {
    auto p = std::make_unique<int>(7);
    int* once = p.get();
    auto q = std::move(p);
    raporla("unique_ptr tasindi", kiyasla(p.get() == once, p == nullptr));
}
static void tasima() {
    std::string s = "kucuk dize eniyilemesini asacak kadar uzun bir metin";
    std::string t = std::move(s);
    belirsiz("std::string tasindi", [&] { s = "yeni deger"; });
}
struct Kayit {
    std::string ad;
    ~Kayit() {}
};
static void bes_kurali() {
    Kayit a{"yikicisi yazilmis bir sinifin uzun adi"};
    std::string once = a.ad;
    Kayit b = std::move(a);
    raporla("yikicili sinifta std::move", kiyasla(a.ad == once, a.ad.empty()));
}
class Tutamac {
public:
    explicit Tutamac(int no) : no_(no) {}
    Tutamac(const Tutamac&) = delete;
    Tutamac& operator=(const Tutamac&) = delete;
    Tutamac(Tutamac&& diger) noexcept : no_(diger.no_) { diger.no_ = -1; }
    Tutamac& operator=(Tutamac&&) = delete;
    int no() const { return no_; }
private:
    int no_;
};
static void kopyalanamaz() {
    Tutamac a(3);
    int once = a.no();
    [[maybe_unused]] Tutamac b = std::move(a);
    raporla("kendi tasima yapicisi", kiyasla(a.no() == once, a.no() == -1));
}
static void kapsayici() {
    std::map<std::string, int> stok{{"elma", 4}};
    auto once = stok;
    int adet = stok["armut"];
    (void)adet;
    raporla("map'te operator[] ile okuma", kiyasla(stok == once, stok.empty()));
}
static void yineleyici() {
    std::vector<int> v{1, 2, 3, 4};
    auto it = v.begin() + 1;
    auto sonraki = v.erase(it);
    belirsiz("erase edilen yineleyici", [&] { it = sonraki; });
}
static void algoritma() {
    std::vector<int> v{5, 1, 4, 2};
    auto once = v;
    std::sort(v.begin(), v.end());
    raporla("std::sort", kiyasla(v == once, v.empty()));
}
static void sil_kaldir() {
    std::vector<int> v{1, 2, 3, 2, 5, 2};
    auto yeni_son = std::remove(v.begin(), v.end(), 2);
    belirsiz("std::remove sonrasi kuyruk", [&] { v.erase(yeni_son, v.end()); });
}
static void lambda() {
    int adim = 0;
    auto say = [adim]() mutable { return ++adim; };
    say();
    say();
    raporla("mutable degerle yakalama", kiyasla(adim == 0, false));
}
static void aralik() {
    std::vector<int> v{1, 2, 3};
    auto once = v;
    int toplam = 0;
    for (int n : v | std::views::transform([](int x) { return x * 10; })) toplam += n;
    (void)toplam;
    raporla("sol degerden view", kiyasla(v == once, v.empty()));
}
static void tip_cikarimi() {
    std::vector<int> v{1, 2, 3};
    auto once = v;
    for (auto& x : v) x *= 2;
    raporla("auto& ile dongu", kiyasla(v == once, v.empty()));
}
static void paylasimli() {
    auto p = std::make_shared<int>(9);
    auto once = p.get();
    auto kopya = p;
    raporla("shared_ptr kopyasi", kiyasla(p.get() == once, p == nullptr));
    std::printf("kullanim sayaci: %ld\n", static_cast<long>(p.use_count()));
    auto tasindi = std::move(p);
    raporla("shared_ptr tasindi", kiyasla(p.get() == once, p == nullptr));
}
static void zayif_referans() {
    auto p = std::make_shared<int>(21);
    std::weak_ptr<int> w = p;
    bool erisilebilir_once = !w.expired();
    p.reset();
    bool erisilebilir_sonra = !w.expired();
    std::printf("zayif referans erisimi: once %s, sonra %s\n",
                erisilebilir_once ? "evet" : "hayir",
                erisilebilir_sonra ? "evet" : "hayir");
}
static void yalniz_move() {
    auto p = std::make_unique<int>(5);
    int* once = p.get();
    auto&& r = std::move(p);
    (void)r;
    raporla("yalniz std::move sonrasi p", kiyasla(p.get() == once, p == nullptr));
}
int main() {
    akilli(); tasima(); bes_kurali(); kopyalanamaz(); kapsayici();
    yineleyici(); algoritma(); sil_kaldir(); lambda(); aralik(); tip_cikarimi();
    ozet();
    paylasimli();
    zayif_referans();
    yalniz_move();
}
```

Bu üç dosya birlikte derlenip çalıştırıldığında şu çıktı basılır:

```text
unique_ptr tasindi               bos
std::string tasindi              belirsiz
yikicili sinifta std::move       aynen
kendi tasima yapicisi            bos
map'te operator[] ile okuma      degisti
erase edilen yineleyici          belirsiz
std::sort                        degisti
std::remove sonrasi kuyruk       belirsiz
mutable degerle yakalama         aynen
sol degerden view                aynen
auto& ile dongu                  degisti
siniflar: aynen 3, degisti 3, bos 2, belirsiz 3
durumu soylenen 8, soylenmeyen 3
shared_ptr kopyasi               aynen
kullanim sayaci: 2
shared_ptr tasindi               bos
zayif referans erisimi: once evet, sonra hayir
yalniz std::move sonrasi p       aynen
```

**KY1.** İlk on üç satır bu kursun ölçtüğü şeyi tanımlar: on bir farklı işlem, hepsi tek bir soruya cevap verir — işlem bittiğinde kaynakta ne kaldı? Cevap dört sınıftan biridir: kaynak öncekiyle **aynen** aynı kalabilir, standardın belirttiği bir şekilde **değişti**rilmiş olabilir, standardın ya da sınıfın kendi garantisiyle **boş**alabilir, ya da standardın hiçbir şey söylemediği **belirsiz** bir duruma düşebilir. On bir işlemin dağılımı — 3 aynen, 3 değişti, 2 boş, 3 belirsiz — bu dersten kursun kapanışına kadar aynı kalacak; her ders bu on birin bir dilimini kendi konusuyla derinleştirecek.

## Taşınan `unique_ptr`: Standardın Sözü Neyi Garanti Eder

Yukarıdaki çıktının ilk satırı `unique_ptr tasindi bos` diyor. Bu, `std::unique_ptr` için standardın açıkça verdiği bir sözdür: bir `unique_ptr` taşıyan yapıcıyla ya da taşıyan atamayla başka bir `unique_ptr`'a taşındığında, kaynak nesne `nullptr` tutar. Bu, akıllı işaretçinin (smart pointer) çıplak bir işaretçiden farkının tam da burada yattığını gösterir: çıplak bir işaretçi taşındıktan sonra ne tuttuğu konusunda hiçbir güvence vermez, `unique_ptr` verir.

Bu güvence yalnızca işaretçinin kendisi için geçerlidir, işaretçinin gösterdiği nesnenin içeriği için değil. Taşınmış bir `unique_ptr` üzerinde `*p` ile erişmeye çalışmak — `p` zaten `nullptr` olduğuna göre — boş bir işaretçiyi dereflemek olur ve bu tanımsız davranıştır (undefined behavior). Bu ders böyle bir çağrıyı ne yazar ne de çalıştırır; tanımlı karşılığı `if (p)` ile önce boşluğu sormaktır. `zayif_referans` işlevindeki `w.expired()` sorgusunun olumsuzu da aynı disiplinin bir başka örneğidir: elde tutulmayan bir kaynağa doğrudan erişmek yerine önce erişilebilir mi diye sorulur.

## Tekil, Paylaşımlı ve Zayıf Sahiplik

`std::make_unique` ve `std::make_shared`, yukarıdaki kodda görüldüğü gibi akıllı işaretçi kurmanın standart yoludur — çıplak `new` çağrısını doğrudan yazmak yerine tercih edilirler. Üçünün taşıdığı sahiplik (ownership) farklıdır. `unique_ptr` tekil sahiplik (unique ownership) taşır: aynı anda yalnız bir `unique_ptr` bir nesneyi tutabilir, bu yüzden kopyalanamaz, yalnız taşınabilir. `shared_ptr` paylaşımlı sahiplik (shared ownership) taşır: birden çok `shared_ptr` aynı nesneyi gösterebilir, bir referans sayımı (reference count) kaç tanesinin gösterdiğini izler.

`paylasimli` işlevi bu farkı iki işlemle gösteriyor. Önce `kopya = p;` ile bir kopya açılıyor; çıktıdaki `shared_ptr kopyasi aynen` satırı, kaynağın (`p`) kopyalamadan sonra da aynı nesneyi göstermeye devam ettiğini söylüyor — `p.get()` değişmedi, `p` hâlâ `nullptr` değil. Hemen ardından basılan `kullanim sayaci: 2`, referans sayımının kopyalamayla birlikte arttığını doğruluyor. Sonra `auto tasindi = std::move(p);` çalışıyor; bu kez `shared_ptr tasindi bos` satırı, taşımanın kopyalamadan tamamen farklı bir sonuç verdiğini gösteriyor — kaynak artık `nullptr`.

**KY2.** Aynı sınıfın (`shared_ptr`) iki farklı işlemi iki farklı sınıfa düşüyor: kopyalama kaynağı **aynen** bırakıyor, taşıma **boş** bırakıyor. Bu, bu kursun ölçtüğü şeyin sınıfın kendisi değil, sınıf üzerinde çalıştırılan işlem olduğunu bir kez daha doğruluyor.

## Zayıf Referans ve Döngüsel Referans

Üçüncü sahiplik türü zayıf sahipliktir (weak ownership): `std::weak_ptr`, bir nesneyi gösterir ama referans sayımını artırmaz, dolayısıyla nesnenin ömrünü uzatmaz. `zayif_referans` işlevi bunu gösteriyor: `w`, `p`'nin gösterdiği nesneye bir zayıf referans (weak reference) tutuyor; `p.reset()` çağrısından önce `w.expired()` yanlış (nesne hâlâ yaşıyor), çağrıdan sonra doğru (nesne artık yok). Çıktıdaki `zayif referans erisimi: once evet, sonra hayir` satırı tam olarak bunu gösteriyor.

Bir `weak_ptr`'ın var olma sebebi, `shared_ptr` ile kurulacak bir döngüsel referansın (reference cycle) önüne geçmektir. İki nesne birbirini `shared_ptr` ile tutarsa, ikisinin de referans sayımı hiçbir zaman sıfıra inmez ve ikisi de asla yok edilmez — bu bir bellek sızıntısıdır. Bu ders böyle bir döngüyü kurup çalıştırmaz; onun yerine tanımlı karşılığını çalıştırır: döngünün bir ucunu `shared_ptr`, diğer ucunu `weak_ptr` yaparak sızıntıyı baştan engellemek. `w.lock()` çağrısı — bu derste doğrudan kullanılmasa da — bir `weak_ptr`'dan güvenli biçimde geçici bir `shared_ptr` almanın standart yoludur; nesne hâlâ yaşıyorsa dolu, yok olduysa boş bir `shared_ptr` döner.

M11/K02'nin ölçtüğü "belleği kim sahipleniyor" sorusu, dillere göre farklı yanıtlanır. Rust'ta (M10/K02) bir değişken taşındığında derleyici o değişkeni artık kullanılamaz sayar ve onu kullanmaya çalışan kodu derlemeyi reddeder — taşınmış değişken bir daha okunamaz, kapsam sonunda onun için yıkım kodu da çalışmaz. C++'ta durum farklıdır: taşınan nesne yaşamaya devam eder, yıkıcısı yine çalışır; derleyici taşınmış bir nesneyi kullanmayı yasaklamaz, yalnızca hangi değeri taşıyacağı bu kursun dört sınıfıyla belirlenir. Sahiplik akıllı işaretçinin türüyle (tekil, paylaşımlı, zayıf) açıkça bildirilir, ama bu kursun kendisinin ölçtüğü şey sahipliğin kimde olduğu değildir — bir işlem bittiğinde kaynağın kendisinde ne kaldığıdır.

## Sınırlayıcı Ölçüm

`yalniz_move` işlevi bu dersin en ince noktasını gösteriyor: `std::move` tek başına hiçbir şeyi taşımaz. `auto&& r = std::move(p);` satırı yalnızca `p`'yi bir sağ değere dönüştürür (bu dönüşümün adı ileride görülecek); herhangi bir taşıyan yapıcı ya da taşıyan atama çağırmaz, çünkü `r` bir referanstır, yeni bir `unique_ptr` inşa etmez. Çıktıdaki `yalniz std::move sonrasi p aynen` satırı bunu doğruluyor — `p.get()` hâlâ eskisiyle aynı, `p` hâlâ `nullptr` değil.

Kaynağı boşaltan `std::move` çağrısının kendisi değildir; kaynağı boşaltan, bir taşıyan yapıcının ya da taşıyan atamanın o dönüştürülmüş değer üzerinden gerçekten çağrılmasıdır. `akilli` işlevindeki `auto q = std::move(p);` satırı `p`'yi boşaltıyor çünkü bu satır yeni bir `unique_ptr` (`q`) inşa ediyor ve bu inşa `unique_ptr`'ın taşıyan yapıcıyı çağırıyor. `yalniz_move`'da böyle bir inşa yok; bu yüzden sonuç farklı sınıfa düşüyor. Bu ayrım, ilerleyen derste bir yazının (`std::move`) kendisinin bir tip dönüşümünden ibaret olduğu söylenirken tekrar karşımıza çıkacak.

## Özet

- Bir işlemden sonra kaynakta ne kaldığı dört sınıfla adlandırılır: **aynen**, **değişti**, **boş**, **belirsiz**; bu kursun on bir dersi bu dört sınıfın farklı dilimlerini ölçer.
- Taşınan bir `unique_ptr`'ın kaynağı `nullptr` olur — bu standardın verdiği bir sözdür; taşınmış işaretçiyi dereflemek tanımsız davranıştır, tanımlı karşılığı `if (p)` sorgusudur.
- `shared_ptr` kopyalaması kaynağı **aynen** bırakır ve referans sayımını artırır; `shared_ptr` taşıması kaynağı **boş** bırakır — aynı sınıf, farklı işlem, farklı sonuç.
- `weak_ptr`, referans sayımını artırmadan bir nesneyi gösterir ve döngüsel referans sızıntısına karşı tanımlı bir karşılık sağlar.
- `std::move` tek başına hiçbir şeyi taşımaz; kaynağı yalnızca gerçekten çağrılan bir taşıyan yapıcı ya da taşıyan atama boşaltır — bu dersin sınırlayıcı ölçümü kaynağı **aynen** bırakan bir `std::move` çağrısıdır.

## Sonraki Adım

Bu ders taşımanın sonucuna baktı: bir `unique_ptr` taşındığında kaynak boşalır, çünkü standart işaretçinin kendisi için bu sözü veriyor. Ama taşınan şey bir işaretçi değil de bir `std::string` gibi kendi içinde veri taşıyan bir tür olduğunda, standart aynı kesinlikte konuşmuyor. Sonraki ders, sağ değer, sol değer ve taşıyan yapıcı kavramlarını açarak standardın daha az kesin konuştuğu bu sonucu ölçüyor.
