---
title: 'C++ Temelleri ve Nesne Tasarımı'
source: 'https://academia.sh/tr/kurslar/cpp-temelleri'
language: tr
updated: '2026-09-26T03:02:45+00:00'
license: 'CC BY-SA 4.0'
lessons: 14
outcomes:
    - 'Sınıf, yapıcı ve yıkıcı yaşam döngüsünü doğru kurar'
    - 'RAII ile kaynak yönetimini otomatikleştirir'
    - 'Kalıtım ve sanal fonksiyonlarla çalışma zamanı çok biçimliliği kurar'
    - 'Bir ifadenin nesnenin kendisiyle mi, bir kopyayla mı, bir parçayla mı, yoksa bir geçiciyle mi çalıştığını ayırt eder ve ölçer'
    - 'Değerle aktarma, dilimleme ve örtük dönüşümün yeni nesne kurduğu yerleri tanır'
---

# C++ Temelleri ve Nesne Tasarımı

C++'ta aynı görünen iki ifade farklı nesnelerle çalışabilir: kurs her düzeneği tek bir soruyla ölçer — bu ifade hangi nesneyle çalışıyor; adı verilen nesnenin kendisiyle mi, ondan kopyalanan yeni bir nesneyle mi, içindeki bir parçayla mı, yoksa satırın sonunda yok olan bir geçiciyle mi? Sınıflar, yapıcılar ve yıkıcılar, RAII, kopyalama, referanslar, kalıtım, sanal fonksiyonlar, istisnalar ve tip dönüşümleri aynı çerçeveden geçirilir.

## Sınıflar ve Kaynaklar

C ile C++'ın ayrıştığı noktalar, erişim denetimi, yapıcılar ve yıkıcılar, RAII, derin ve sığ kopya, referanslar ve işleç aşırı yükleme — her biri bir ifadenin hangi nesneyle çalıştığı sorusuyla birlikte.

### C ve C++ Farkları

Source: https://academia.sh/tr/kurslar/cpp-temelleri/c-ve-cpp-farklari

C'de değerle aktarılan bir yapının C++'ta hangi nesneyle çalıştığını ölçen bir çekirdek koşumuyla kursun dört ölçüm sınıfını tanıtır.

M11/K03'te "Nesne Benzeri Tasarım" bir yapıyı ve ona bağlı işlev işaretçilerini elle bir
araya getirerek nesne davranışını C'de taklit etti; hangi işlevin çalışacağı elle kurulan
bir tabloda saklanıyordu. Bu kurs aynı soruyu C++'ın kendi dil öğeleriyle sorar: **bir
ifade, tam olarak hangi nesneyle çalışır** — adı verilen nesnenin kendisiyle mi, ondan
kopyalanmış yeni bir nesneyle mi, içindeki bir parçayla mı, yoksa adsız ve kısa ömürlü bir
nesneyle mi?

#### Ölçü Ekseni

Kurs boyunca her ders bir ifadeyi dört sınıftan birine yerleştirir:

- **kendisi** — ifade, adı verilen nesnenin tamamıyla çalışır.
- **kopya** — ifade, adı verilen nesneden kopyalanarak kurulmuş yeni bir nesneyle çalışır.
- **parça** — ifade, adı verilen nesnenin içindeki bir alt nesneyle çalışır.
- **geçici** — ifade, adsız ve tam ifadenin sonunda yok olan yeni bir nesneyle çalışır.

Bu dört sınıf tahmin değil, koşum sonucudur: aşağıdaki çekirdek her ifadeyi adres,
kopyalayan yapıcı kaynağı ve nesne ömrü üzerinden izler ve sınıfı kendisi belirler.

#### Çekirdeğin Tam Koşumu

Çekirdek üç dosyadır. `iz.hpp` ve `iz.cpp` izleme altyapısını taşır: her nesne kuruluşta bir
sıra numarası alır, kopyalanırsa kaynağının numarasını saklar; `dokun` ifadenin gerçekten
çalıştığı nesneyi kaydeder, `bitir` beyan edilen sınıfla ölçüleni karşılaştırıp satırı
basar. `hangisi.cpp` bu kursun on dört dersinin tümüne ait on dört ölçümü tek `main`'de
koşturur.

```cpp
// iz.hpp — bir ifadenin hangi nesneyle calistigini olcen iz
#ifndef IZ_HPP
#define IZ_HPP

#include <cstddef>
#include <typeinfo>

enum class Sinif { kendisi, kopya, parca, gecici };

// Izlenen her nesne bu tabandan turer: kurulusta bir sira numarasi alir,
// kopyalanirsa kaynaginin numarasini saklar.
class Iz {
public:
    Iz();
    Iz(const Iz& kaynak);
    Iz& operator=(const Iz& kaynak);
    ~Iz();
    int no() const { return no_; }
    int kaynak() const { return kaynak_; }

private:
    int no_;
    int kaynak_;
};

void baslat_kayit(const void* adres, std::size_t boy, const std::type_info& tip, int no);
void dokun_kayit(const void* adres, std::size_t boy, const std::type_info& tip,
                 const Iz* iz, int kaynak);

// Adi verilen nesneyi kaydeder: olcum bu nesneye gore yapilir.
template <class T>
void baslat(const T& butun) {
    baslat_kayit(&butun, sizeof butun, typeid(T), butun.no());
}

// Ifadenin gercekten calistigi nesneyi kaydeder ve siniflandirir.
template <class T>
void dokun(const T& nesne) {
    dokun_kayit(&nesne, sizeof nesne, typeid(T), &static_cast<const Iz&>(nesne),
                nesne.kaynak());
}

// Olcumu kapatir, beyan edilen sinifla olculeni karsilastirip satiri basar.
void bitir(const char* ifade, Sinif beyan);

// Sinif basina sayilari ve var olan / yeni nesne toplamini basar.
void ozet();

#endif
```

```cpp
// iz.cpp — sayilar ve olcum durumu yalniz bu birimde tutulur
#include "iz.hpp"

#include <cstdio>
#include <functional>

namespace {

int olusan = 0;
const char* const adlar[] = { "kendisi", "kopya", "parca", "gecici" };
int sayilar[4] = {};
int var_olan = 0;
int yeni = 0;

const char* butun_adres = nullptr;
std::size_t butun_boy = 0;
const std::type_info* butun_tip = nullptr;
int butun_no = 0;

bool dokunuldu = false;
bool ayni = false;
bool icinde = false;
bool kopyasi = false;
bool bitti = false;
const Iz* izlenen = nullptr;

}  // namespace

Iz::Iz() : no_(++olusan), kaynak_(0) {}

Iz::Iz(const Iz& kaynak) : no_(++olusan), kaynak_(kaynak.no_) {}

Iz& Iz::operator=(const Iz&) {
    return *this;  // atama nesneyi degistirir ama yeni nesne kurmaz
}

Iz::~Iz() {
    if (this == izlenen) {
        bitti = true;
        izlenen = nullptr;
    }
}

void baslat_kayit(const void* adres, std::size_t boy, const std::type_info& tip, int no) {
    butun_adres = static_cast<const char*>(adres);
    butun_boy = boy;
    butun_tip = &tip;
    butun_no = no;
    dokunuldu = ayni = icinde = kopyasi = bitti = false;
    izlenen = nullptr;
}

void dokun_kayit(const void* adres, std::size_t boy, const std::type_info& tip,
                 const Iz* iz, int kaynak) {
    const char* bas = static_cast<const char*>(adres);
    std::less<const char*> once;
    dokunuldu = true;
    ayni = bas == butun_adres && boy == butun_boy && tip == *butun_tip;
    icinde = !once(bas, butun_adres) && !once(butun_adres + butun_boy, bas + boy);
    kopyasi = kaynak == butun_no;
    bitti = false;
    izlenen = iz;
}

void bitir(const char* ifade, Sinif beyan) {
    int olculen = -1;
    if (dokunuldu) {
        if (ayni) {
            olculen = static_cast<int>(Sinif::kendisi);
        } else if (icinde) {
            olculen = static_cast<int>(Sinif::parca);
        } else if (kopyasi) {
            olculen = static_cast<int>(Sinif::kopya);
        } else if (bitti) {
            olculen = static_cast<int>(Sinif::gecici);
        }
    }
    int b = static_cast<int>(beyan);
    bool var = beyan == Sinif::kendisi || beyan == Sinif::parca;
    sayilar[b]++;
    if (var) {
        var_olan++;
    } else {
        yeni++;
    }
    std::printf("%-36s %-5s %s%s\n", ifade, var ? "var" : "yeni", adlar[b],
                olculen == b ? "" : "  TUTARSIZ");
    izlenen = nullptr;
}

void ozet() {
    std::printf("siniflar: kendisi %d, kopya %d, parca %d, gecici %d\n",
                sayilar[0], sayilar[1], sayilar[2], sayilar[3]);
    std::printf("var olan %d, yeni %d\n", var_olan, yeni);
}
```

```cpp
// hangisi.cpp — on dort ifade, her biri icin tek soru: hangi nesne
#include <algorithm>

#include "iz.hpp"

/* 1. C ve C++ farklari: degerle aktarilan yapi */
struct Nokta : Iz {
    int x = 0;
    int y = 0;
};
static void kaydir(Nokta p) {
    p.x += 1;
    dokun(p);
}
static void c_farki() {
    Nokta n;
    baslat(n);
    kaydir(n);
    bitir("degerle aktarilan yapi", Sinif::kopya);
}

/* 2. Erisim denetimi: uye islevin calistigi nesne */
class Hesap : public Iz {
public:
    void yatir(int miktar) {
        bakiye_ += miktar;
        dokun(*this);
    }
    int bakiye() const { return bakiye_; }

private:
    int bakiye_ = 0;
};
static void erisim() {
    Hesap h;
    baslat(h);
    h.yatir(50);
    bitir("uye islevin this'i", Sinif::kendisi);
}

/* 3. Yapicilar: donusturen yapicinin kurdugu nesne */
class Tutar : public Iz {
public:
    Tutar(int deger) : deger_(deger) {}  // explicit degil: int'ten donusturur
    int deger() const { return deger_; }

private:
    int deger_;
};
static int goster(const Tutar& t) {
    dokun(t);
    return t.deger();
}
static void yapici() {
    Tutar t(3);
    baslat(t);
    goster(5);
    bitir("int'ten kurulan arguman", Sinif::gecici);
}

/* 4. RAII: koruyucunun biraktigi uye */
class Kaynak : public Iz {
public:
    void edin() { tutuluyor_ = true; }
    void birak() {
        tutuluyor_ = false;
        dokun(*this);
    }

private:
    bool tutuluyor_ = false;
};
class Koruyucu {
public:
    explicit Koruyucu(Kaynak& k) : k_(k) { k_.edin(); }
    ~Koruyucu() { k_.birak(); }
    Koruyucu(const Koruyucu&) = delete;
    Koruyucu& operator=(const Koruyucu&) = delete;

private:
    Kaynak& k_;
};
class Sayac : public Iz {
public:
    void artir() {
        Koruyucu g(kaynak_);
        deger_++;
    }

private:
    Kaynak kaynak_;
    int deger_ = 0;
};
static void raii() {
    Sayac s;
    baslat(s);
    s.artir();
    bitir("koruyucunun biraktigi uye", Sinif::parca);
}

/* 5. Kopyalama: derin kopyalanan tampon */
class Tampon : public Iz {
public:
    explicit Tampon(int boy) : boy_(boy), veri_(new int[boy]()) {}
    Tampon(const Tampon& k) : Iz(k), boy_(k.boy_), veri_(new int[k.boy_]) {
        std::copy(k.veri_, k.veri_ + k.boy_, veri_);
    }
    Tampon& operator=(const Tampon& k) {
        if (this != &k) {
            int* yeni = new int[k.boy_];
            std::copy(k.veri_, k.veri_ + k.boy_, yeni);
            delete[] veri_;
            veri_ = yeni;
            boy_ = k.boy_;
        }
        return *this;
    }
    ~Tampon() { delete[] veri_; }
    void yaz(int i, int deger) {
        veri_[i] = deger;
        dokun(*this);
    }
    int oku(int i) const { return veri_[i]; }

private:
    int boy_;
    int* veri_;
};
static void kopyalama() {
    Tampon a(3);
    baslat(a);
    Tampon b(a);
    b.yaz(0, 9);
    bitir("derin kopyalanan tampon", Sinif::kopya);
}

/* 6. Referanslar: referansla aktarilan nesne */
static void tasi(Nokta& p) {
    p.y += 1;
    dokun(p);
}
static void referans() {
    Nokta n;
    baslat(n);
    tasi(n);
    bitir("referansla aktarilan yapi", Sinif::kendisi);
}

/* 7. Islec asiri yukleme: a + b'nin sonucu */
class Para : public Iz {
public:
    explicit Para(int kurus) : kurus_(kurus) {}
    Para operator+(const Para& b) const { return Para(kurus_ + b.kurus_); }
    Para& operator+=(const Para& b) {
        kurus_ += b.kurus_;
        return *this;
    }
    int kurus() const { return kurus_; }

private:
    int kurus_;
};
static int yaz_para(const Para& p) {
    dokun(p);
    return p.kurus();
}
static void islec() {
    Para a(150);
    Para b(250);
    baslat(a);
    yaz_para(a + b);
    bitir("a + b sonucu", Sinif::gecici);
}

/* 8. Kalitim: taban sinifin islevi */
class Arac : public Iz {
public:
    void hizlan() {
        hiz_++;
        dokun(*this);
    }

private:
    int hiz_ = 0;
};
class Kamyon : public Arac {
public:
    int yuk() const { return yuk_; }

private:
    int yuk_ = 0;
};
static void kalitim() {
    Kamyon k;
    baslat(k);
    k.hizlan();
    bitir("taban sinifin islevi", Sinif::parca);
}

/* 9. Sanal fonksiyonlar: taban referansindan sanal cagri */
class Sekil : public Iz {
public:
    virtual ~Sekil() = default;
    virtual void ciz() const { dokun(*this); }
};
class Daire : public Sekil {
public:
    void ciz() const override { dokun(*this); }

private:
    int yaricap_ = 1;
};
static void sanal() {
    Daire d;
    baslat(d);
    const Sekil& s = d;
    s.ciz();
    bitir("taban referansindan sanal cagri", Sinif::kendisi);
}

/* 10. Soyut siniflar: arayuz uzerinden cagri */
class Kaydedici : public Iz {
public:
    virtual ~Kaydedici() = default;
    virtual void kaydet() = 0;
};
class SatirKaydedici final : public Kaydedici {
public:
    void kaydet() override {
        satir_++;
        dokun(*this);
    }

private:
    int satir_ = 0;
};
static void kullan(Kaydedici& k) {
    k.kaydet();
}
static void soyut() {
    SatirKaydedici k;
    baslat(k);
    kullan(k);
    bitir("arayuz uzerinden cagri", Sinif::kendisi);
}

/* 11. Coklu kalitim: sanal tabanin tek alt nesnesi */
class Cihaz : public Iz {
public:
    void ac() {
        acik_ = true;
        dokun(*this);
    }

private:
    bool acik_ = false;
};
class Yazici : public virtual Cihaz {
    int sayfa_ = 0;
};
class Tarayici : public virtual Cihaz {
    int cozunurluk_ = 0;
};
class Cokislevli : public Yazici, public Tarayici {};
static void elmas() {
    Cokislevli c;
    baslat(c);
    c.ac();
    bitir("sanal tabanin alt nesnesi", Sinif::parca);
}

/* 12. Istisnalar: degerle yakalanan istisna */
class Hata : public Iz {
public:
    explicit Hata(int kod) : kod_(kod) {}
    int kod() const { return kod_; }

private:
    int kod_;
};
static void istisna() {
    const Hata* firlatilan = nullptr;
    try {
        try {
            throw Hata(7);
        } catch (const Hata& ic) {
            firlatilan = &ic;
            throw;
        }
    } catch (Hata e) {
        baslat(*firlatilan);
        dokun(e);
        bitir("degerle yakalanan istisna", Sinif::kopya);
    }
}

/* 13. Ad alanlari: using bildirimiyle anilan nesne */
namespace muhasebe {
class Defter : public Iz {
public:
    void isle() {
        kayit_++;
        dokun(*this);
    }

private:
    int kayit_ = 0;
};
Defter ana_defter;
}  // namespace muhasebe

static void ad_alani() {
    using muhasebe::ana_defter;
    baslat(muhasebe::ana_defter);
    ana_defter.isle();
    bitir("using bildirimiyle anilan nesne", Sinif::kendisi);
}

/* 14. Tip donusumleri: tabana donusturulen referans */
class Olcer : public Iz {
public:
    void oku() const { dokun(*this); }
};
class Termometre : public Olcer {
public:
    int derece() const { return derece_; }

private:
    int derece_ = 20;
};
static void donusum() {
    Termometre t;
    baslat(t);
    static_cast<const Olcer&>(t).oku();
    bitir("tabana donusturulen referans", Sinif::parca);
}

int main() {
    c_farki();
    erisim();
    yapici();
    raii();
    kopyalama();
    referans();
    islec();
    kalitim();
    sanal();
    soyut();
    elmas();
    istisna();
    ad_alani();
    donusum();
    ozet();
    return 0;
}
```

```text
degerle aktarilan yapi               yeni  kopya
uye islevin this'i                   var   kendisi
int'ten kurulan arguman              yeni  gecici
koruyucunun biraktigi uye            var   parca
derin kopyalanan tampon              yeni  kopya
referansla aktarilan yapi            var   kendisi
a + b sonucu                         yeni  gecici
taban sinifin islevi                 var   parca
taban referansindan sanal cagri      var   kendisi
arayuz uzerinden cagri               var   kendisi
sanal tabanin alt nesnesi            var   parca
degerle yakalanan istisna            yeni  kopya
using bildirimiyle anilan nesne      var   kendisi
tabana donusturulen referans         var   parca
siniflar: kendisi 5, kopya 3, parca 4, gecici 2
var olan 9, yeni 5
```

**SK1.** On dört ifadenin beşi kendisiyle, üçü bir kopyayla, dördü bir parçayla, ikisi bir
geçiciyle çalıştı; kursun dört sınıfının adı ve dağılımı buradan doğar. İlk satır bu dersin
konusudur: `kaydir(Nokta p)` değerle aldığı `p` üzerinde çalışır, `p` `n`'den kopyalanarak
kurulmuştur — `kaydir(n)` içindeki `p.x += 1` çağıranın `n`'sine ulaşmaz. C'de bir `struct`'ı
değerle almak da aynı sonucu verir; C++ bu davranışı değiştirmedi, üzerine yeni bir seçenek
ekledi.

#### Değerle Aktarılan Yapının Derinleştirilmesi

C++'ın C'ye eklediği referans parametresi aynı çağrıyı farklı bir sınıfa taşır. Aşağıdaki
ölçüm `kaydir` ile `tasi`'yi kendi `main`'inde yan yana koşturur; ikisi de `n` üzerinden
çağrılır, ikisi de kaynakta `(n)` yazımıyla görünür, ama biri değerle biri referansla alır.

```cpp
// deger-referans.cpp — ayni cagri yazimi, farkli parametre bildirimi
#include <cstdio>

struct Nokta {
    int x = 0;
};

static void kaydir(Nokta p, const Nokta* disaridaki) {
    p.x += 1;
    std::printf("kaydir: p disaridaki nesneyle ayni mi %s\n",
                &p == disaridaki ? "evet" : "hayir");
}

static void tasi(Nokta& p, const Nokta* disaridaki) {
    p.x += 1;
    std::printf("tasi: p disaridaki nesneyle ayni mi %s\n",
                &p == disaridaki ? "evet" : "hayir");
}

int main() {
    Nokta n;
    kaydir(n, &n);
    tasi(n, &n);
    return 0;
}
```

```text
kaydir: p disaridaki nesneyle ayni mi hayir
tasi: p disaridaki nesneyle ayni mi evet
```

**SK2.** `kaydir(n)` içindeki `p`, çağırandaki `n`'den ayrı bir nesnedir — kopya sınıfı.
`tasi(n)` içindeki `p`, çağırandaki `n`'nin kendisidir — kendisi sınıfı. Çağrı yazımı
ikisinde de `(n)`'dir; farkı yaratan tek şey işlevin **bildirimidir**: parametre tipi
`Nokta` mı `Nokta&` mı.

#### C++'ın C'den Farkları

Bu ders yalnız değerle aktarımı ölçtü; C++'ın C'ye eklediği öğelerin geri kalanı kursun
sonraki derslerinde tek tek ölçülür. Kısaca sayılırsa: `class` ve erişim denetimi, referans
tipi, işleç aşırı yükleme, `new`/`delete`, ad alanları, ayrı bir `bool` tipi ve C'den daha
sıkı bir tip denetimi — örneğin `void*`'tan başka bir işaretçi tipine örtük dönüşüm C'de
serbesttir, C++'ta derleme hatasıdır.

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

Bu dersin ölçtüğü ifade **kopya** sınıfındadır, ama ölçümün kendisi bir sınırı da gösterir:
çağrı noktası hangi sınıfla çalışılacağını **söylemez**. `kaydir(n)` ile `tasi(n)` kaynakta
birebir aynı görünür — ikisi de bir tanımlayıcıyı parantez içinde `n` ile çağırır. Hangi
nesneyle çalışıldığını yalnız işlevin bildirimindeki parametre tipi belirler; okuyan biri bu
kararı çağrı satırından değil, imzadan çıkarmak zorundadır.

#### Özet

- Kursun ölçü ekseni: bir ifade **kendisi**, **kopya**, **parça** ya da **geçici**
  sınıflarından biriyle çalışır; on dört ölçüm bu dağılımı 5/3/4/2 olarak sabitler.
- Bu dersin ölçtüğü ifade **kopya** sınıfındadır: değerle aktarılan `Nokta`, çağıranın
  nesnesinden ayrı bir kopyayla çalışır.
- Aynı `(n)` çağrı yazımı, parametre referans olduğunda **kendisi** sınıfına geçer; sınıfı
  belirleyen çağrı değil bildirimdir.
- C++'ın C'ye eklediği öğeler (sınıf, referans, aşırı yükleme, `new`/`delete`, ad alanı,
  `bool`, daha sıkı tip denetimi) kısaca sayıldı; her biri ilgili derste derinleşir.
- Derleme uyarısızdır ve çıktı üç ayrı koşumda birebir aynıdır; ölçüm tahmine değil koşuma
  dayanır.

#### Sonraki Adım

Bu ders bir işlevin parametre tipine göre hangi nesneyle çalıştığını gösterdi; bir sonraki
ders aynı soruyu bir **üye işlev** için sorar — `h.yatir(50)` çağrıldığında `this`'in hangi
nesneyi gösterdiğini ve `private` erişiminin nesne değil sınıf düzeyinde çalıştığını ölçer.

### Sınıflar ve Erişim Denetimi

Source: https://academia.sh/tr/kurslar/cpp-temelleri/siniflar-ve-erisim-denetimi

Bir üye işlevin hangi nesneyle çalıştığını ve erişim denetiminin nesne değil sınıf düzeyinde işlediğini ölçer.

Önceki ders bir serbest işlevin parametresine bakarak hangi nesneyle çalıştığını ölçtü:
`kaydir(Nokta n)` kendi kopyasıyla çalışıyordu, `tasi(Nokta* p)` çağıranın nesnesine
adresle ulaşıyordu. Bir **üye işlev**in de bir parametresi vardır, ama derleyici onu örtük
geçirir: çağrının yapıldığı nesnenin adresi. Bu örtük parametrenin adı `this`'tir ve bir üye
işlevin gövdesinde her zaman kullanılabilir — yazılmasa bile oradadır. Bu ders o örtük
parametreyi aynı çekirdekle ölçer: bir üye işlev çağrıldığında `this` hangi nesneyi
gösteriyor, ve bu bilgi erişim denetimiyle nasıl bir araya geliyor?

#### Sınıf ve Erişim Denetimi

`class` ile `struct` C++'ta aynı şeydir; ikisi de üye değişken ve üye işlev taşıyabilen bir
tür tanımlar, ikisi de kalıtım alabilir, ikisi de yapıcı ve yıkıcı çalıştırır. Aralarındaki
tek fark varsayılan erişimdir. `struct` üyeleri varsayılan olarak `public`, `class` üyeleri
varsayılan olarak `private`'tır — başka hiçbir davranış farkı yoktur. Bu, C'den gelen bir
mirastır: C'de `struct` yalnız veri taşıyan bir kayıttı, işlev taşımazdı; C++ `struct`'a üye
işlev ekleme yeteneğini verirken geriye dönük uyumluluk için varsayılan erişimi `public`
bıraktı. Bir C++ kod tabanında `struct`'ın genellikle yalnız veri taşıyan, davranışı az
türler için, `class`'ın ise davranışı erişim denetimiyle koruyan türler için kullanılması bir
gelenektir — dilin kendisi bunu zorlamaz.

Bir sınıf üç erişim düzeyi bildirir:

- `public` — sınıfın dışından, herhangi bir kodun herhangi bir yerinden erişilebilir. Bir
  sınıfın dışarıya sunduğu **arayüz** buradadır.
- `private` — yalnız sınıfın kendi üye işlevlerinden (ve `friend` olarak bildirilmiş işlev ya
  da sınıflardan) erişilebilir. Bir sınıfın kendi iç durumunu tuttuğu veri üyeleri genellikle
  buradadır.
- `protected` — kendi üye işlevlerinden ve türeyen sınıflardan erişilebilir; `private`'tan
  farkı yalnız kalıtımda ortaya çıkar. Kalıtım M11/K04'ün ikinci konusuna kadar bu kursta
  kullanılmaz, bu yüzden `protected` burada yalnız adıyla anılır.

Bir erişim düzeyi bildirimi (`public:`, `private:` ya da `protected:`) bir sınıf gövdesinde
o noktadan sonraki tüm üyelere uygulanır, ta ki bir sonraki erişim düzeyi bildirimine kadar.
`Hesap` sınıfında `public:` altındaki üç üye işlev dışarıdan çağrılabilir, `private:` altındaki
`bakiye_` çağrılamaz — aynı sınıf gövdesinde birden çok `public:`/`private:` bloğu da
bulunabilir, derleyici hepsini birleştirir.

Bir sınıf **veri üyesi** (nesnenin her örneğinde ayrı bir kopyası olan değişken — `bakiye_`
her `Hesap` nesnesinde kendi değerini taşır) ve **üye işlev** (sınıfın tüm örnekleri arasında
paylaşılan, çağrıldığı örneğin veri üyelerine `this` üzerinden erişen kod — `yatir`'in
makine kodu bir kez üretilir, her `Hesap` nesnesi için ayrı ayrı değil) taşır. Bir üye işlev
`const` işaretliyse `this`'in kendisi `const`'tur: işlev veri üyelerini değiştiremez, yalnız
okuyabilir. `bakiye()` ve `esit_mi` bu yüzden `const`'tur — ikisi de `bakiye_`'yi okur ama
değiştirmez; `yatir` `const` değildir çünkü `bakiye_`'yi değiştirir. Bir `const` nesne
üzerinden yalnız `const` üye işlevler çağrılabilir; `const` olmayan bir üye işlevi `const`
bir nesne üzerinden çağırmak derleme hatasıdır, çünkü derleyici `this`'in işlev içinde
değiştirilmeyeceğini garanti edemez.

`friend` bildirimi bir işleve ya da sınıfa, üyesi olmadığı bir sınıfın `private` üyelerine
erişim tanır — erişim denetiminin tek istisnasıdır ve sınıfın kendi bildirimiyle açıkça
verilir, dışarıdan alınamaz. Bu kursta `friend` yalnız bir kez, `operator<<`'ın bir sınıfın
`private` veri üyesini okuyabilmesi için kullanılır (İşleç Aşırı Yükleme dersinde); burada
yalnız adıyla anılır.

Kapsülleme — bir sınıfın iç durumunu dışarıdan gizleyip yalnız işlevler üzerinden erişim
sunması — M08'de zaten işlendi; burada yeniden öğretilmez, yalnız C++'ın `public`/`private`
sözdizimiyle uygulanır. `Hesap` bu fikri uygular: `bakiye_`'ye dışarıdan
doğrudan yazılamaz, yalnız `yatir` üzerinden değiştirilebilir.

#### Üye İşlevin `this`'i

`this`, bir üye işlevin gövdesinde örtük olarak tanımlı bir işaretçidir; tipi çağrıldığı
sınıfa göre belirlenir. `Hesap::yatir` içinde `this`'in tipi `Hesap* const`'tur — işaretçinin
kendisi değiştirilemez (başka bir `Hesap`'ı göstermeye başlayamaz), ama gösterdiği nesne
değiştirilebilir. `const` işaretli bir üye işlevde — `Hesap::bakiye() const` gibi — `this`'in
tipi `const Hesap* const`'tur: hem işaretçi hem gösterdiği nesne değiştirilemez, bu yüzden
`const` bir üye işlev veri üyelerine yalnız okuma erişimi kazanır. Kod bu işaretçiyi elle
yazmaz — `bakiye_ += miktar` derleyici tarafından `this->bakiye_ += miktar` olarak okunur —
ama aşağıdaki ölçüm `this`'in gerçekten çağrılan nesneyi gösterdiğini, başka bir nesneyi
değil, açıkça gösterir: `h.yatir(50)` çağrısından önce alınan `&h` adresiyle, işlev içinde
üstü kapalı kullanılan `this`'in eşdeğeri karşılaştırılır.

```cpp
// hesap.hpp — bakiyesi private olan bir hesap
#ifndef HESAP_HPP
#define HESAP_HPP

class Hesap {
public:
    void yatir(int miktar) { bakiye_ += miktar; }
    int bakiye() const { return bakiye_; }
    bool esit_mi(const Hesap& digeri) const { return bakiye_ == digeri.bakiye_; }

private:
    int bakiye_ = 0;
};

#endif
```

```cpp
// this-olcumu.cpp — uye islevin this'i cagrilan nesneyi mi gosteriyor
#include <cstdio>

#include "hesap.hpp"

int main() {
    Hesap h;
    Hesap* onceki = &h;

    h.yatir(50);
    std::printf("this cagrilan nesneyi gosteriyor mu %s\n", onceki == &h ? "evet" : "hayir");
    std::printf("bakiye %d\n", h.bakiye());
    return 0;
}
```

```text
this cagrilan nesneyi gosteriyor mu evet
bakiye 50
```

**SK3.** `h.yatir(50)` çağrısında derleyici `this`'e `&h`'yi geçirir; `yatir` içindeki
`bakiye_ += miktar` satırı `h.bakiye_`'yi değiştirir, başka hiçbir `Hesap`'ı değil. Bu ifade
**kendisi** sınıfındadır: `h`'nin kendisiyle çalışır.

#### Erişim Denetimi Nesneyi Değil Sınıfı Sorar

`bakiye_` `private`'tır; `Hesap`'ın dışından `h.bakiye_` yazılamaz — `main` içinde böyle bir
satır denense derleyici reddeder, çünkü `main` `Hesap` sınıfının bir üyesi değildir. Ama
`Hesap`'ın kendi bir üye işlevi, **başka bir** `Hesap` nesnesinin `bakiye_`'sine doğrudan
erişebilir — erişim denetimi nesneyi değil, sınıfı sorar. `esit_mi` bunu gösterir:
`digeri.bakiye_` çağıranın kendi `bakiye_`'si değildir, ama aynı sınıftan olduğu için
derlenir. Bu, birçok dilde geçerli olmayan bir C++ kuralıdır — bazı dillerde erişim denetimi
nesne düzeyinde işler ve bir nesne yalnız kendi verisine dokunabilir; C++'ta sınır sınıfın
kendisidir, tek bir nesne değil.

```cpp
// esitlik-olcumu.cpp — bir uye islev baska bir nesnenin private uyesine erisiyor
#include <cstdio>

#include "hesap.hpp"

int main() {
    Hesap a;
    Hesap b;
    a.yatir(30);
    b.yatir(30);

    std::printf("a ile b esit mi %s\n", a.esit_mi(b) ? "evet" : "hayir");

    b.yatir(1);
    std::printf("a ile b esit mi %s\n", a.esit_mi(b) ? "evet" : "hayir");
    return 0;
}
```

```text
a ile b esit mi evet
a ile b esit mi hayir
```

**SK4.** `a.esit_mi(b)` içindeki `digeri.bakiye_` erişimi `b`'nin `private` üyesine
`a`'nın üye işlevinden yapılır; derleyici bunu yalnız `digeri`'nin tipine (`const Hesap&`)
bakarak kabul eder, `a` ile `b`'nin aynı nesne olup olmadığına bakmaz. Erişim kararı
**derleme zamanında**, sınıf bilgisine göre verilir.

Bu, birçok C++ öğrencisinin ilk şaşırdığı noktalardan biridir: `private`'ın "yalnız bu nesne
kendi verisine erişebilir" anlamına geldiği sanılır, ama kural budur — "yalnız bu **sınıfın**
üye işlevleri erişebilir." `esit_mi`'nin gövdesi derlenirken derleyici yalnız şunu bilir:
`digeri` bir `const Hesap&`'tır, ve o an derlenen işlev `Hesap` sınıfının bir üyesidir; bu iki
bilgi `digeri.bakiye_` erişimine izin vermek için yeterlidir. `a` ile `digeri`'nin (yani
`b`'nin) aynı çalışma zamanı nesnesi olup olmadığı derleme zamanında bilinmez ve erişim
kararına hiç katılmaz — nitekim ikinci çağrıda `a` ile `b` ayrı nesnelerdir ve erişim yine de
derlenir.

#### private Neden Nesneyi Değil Sınıfı Korur

Bu kural rastgele değildir; sınıf yazarının **değişmezlerini** (invariant — bir nesnenin her
zaman doğru kalması gereken durumu, örneğin `bakiye_`'nin hiçbir zaman negatife düşmemesi)
korumasını mümkün kılar. Eğer `private` yalnız "başka nesnelerden değil, kendi nesnenden
erişilebilir" anlamına gelseydi, `esit_mi` gibi iki nesneyi karşılaştıran ya da bir nesneden
diğerine veri kopyalayan işlevler yazılamazdı — her üye işlev yalnız kendi `this`'ine
bakabilirdi. Sınıf düzeyinde erişim, bir sınıfın kendi üye işlevleri arasında güvenle veri
paylaşmasını sağlarken, sınıfın dışındaki koda aynı veriyi kapatır. Sınırı çizen nesne değil,
**sınıfın kaynak dosyasındaki bildirimdir.**

Bunun karşıtı, alanı dışarıya açık bırakılmış bir yapıdır: böyle bir kayıtta çağıran kod o
alanı herhangi bir denetimden geçirmeden değiştirebilir. `Hesap` aynı hatayı yapmaz —
`bakiye_`'ye dışarıdan yazma yolu yoktur, yalnız `yatir` üzerinden değiştirilebilir. `yatir`
bugün `bakiye_ += miktar` dışında bir şey yapmıyor, ama `private` sayesinde ileride negatif
bir `miktar`'ı reddetmek ya da bir üst sınırı denetlemek gibi bir kural eklense, bu kural
her çağıranı değiştirmeden yalnız `yatir`'in içine yazılabilir — çünkü `bakiye_`'ye giden
**tek yol** `yatir`'dir. Bu, `private`'ın sağladığı asıl mühendislik değeridir: sınıfın iç
temsilini değiştirme özgürlüğü, dışarıdaki koda dokunmadan.

#### const Nesne Üzerinden Çağrı Seçimi

`bakiye()` ve `esit_mi` neden `const` işaretlidir, `yatir` neden değildir sorusunun bir
pratik sonucu vardır: bir `const Hesap&` parametre alan bir işlev, o parametre üzerinden
yalnız `const` üye işlevleri çağırabilir. `esit_mi`'nin imzası `bool esit_mi(const Hesap&
digeri) const`'tur; gövdesi içinde `digeri.bakiye_`'ye doğrudan erişir, ama `digeri.bakiye()`
çağırsaydı da derlenirdi — çünkü `bakiye()` `const`'tur ve `const` bir referans üzerinden
çağrılabilir. `digeri.yatir(10)` denenmiş olsaydı derlenmezdi: `yatir` `const` değildir, ve
`const` bir nesne (ya da `const` bir referansın gösterdiği nesne) üzerinden `const` olmayan
bir üye işlev çağrılamaz. Bu kural, bir işlevin imzasına `const` yazmasının yalnız belgeleyici
bir not olmadığını, derleyicinin gerçekten denetlediği bir sözleşme olduğunu gösterir:
`esit_mi`'nin `digeri`'yi değiştirmeyeceği, işlevin kendi imzasından derleme zamanında
doğrulanır.

Aynı kural `this` için de geçerlidir. `Hesap::esit_mi` `const` işaretli olduğu için kendi
`this`'i de `const Hesap* const`'tur; bu işlevin gövdesi içinde `bakiye_ += 1` gibi bir
değişiklik denense — kendi nesnesi üzerinde bile — derleyici reddeder. `const` bir üye
işlev, ne kendi nesnesini ne de `const` bir referansla aldığı başka bir nesneyi
değiştirebilir; değiştirebileceği tek şey yerel değişkenler ve `mutable` işaretli veri
üyeleridir (`mutable`, önbellekleme gibi nadir durumlar için ayrılmış bir işaretleyicidir ve
bu kursta kullanılmaz).

#### Bildirim Sırası ve Erişim Düzeyi Blokları

`Hesap` sınıfında erişim düzeyi bildirimleri belli bir yerleşim sırası izler: önce `public:`
altında dışarıya sunulan üye işlevler, sonra `private:` altında veri üyesi. Bu sıra dilin bir
kuralı değildir — derleyici için `private:` üstte, `public:` altta olsaydı da aynı sonucu
verirdi — ama okunabilirlik için yaygın bir alışkanlıktır: bir sınıfı okuyan kişi önce onun
sunduğu arayüzü, sonra iç temsilini görür. Ters sıra (önce `private` veri, sonra `public`
işlevler) de geçerlidir ve bazı kod tabanlarında tercih edilir; bu ders `public`-önce
sırasını izler ve kursun geri kalanında tutarlı kalır.

Bir erişim düzeyi bildirimi yalnız bir kez kullanılmak zorunda değildir — aynı sınıf gövdesi
içinde `public:` ve `private:` birden fazla kez tekrarlanabilir, her biri kendinden sonraki
bloğu kapsar. `Hesap` bunu yapmaz; sınıf küçük olduğu için tek bir `public:` ve tek bir
`private:` yeterlidir. Daha büyük bir sınıfta, ilgili üyeleri bir arada tutmak için birden
çok blok kullanmak — örneğin yapıcılar için ayrı bir `public:`, sorgulayıcı işlevler için
ayrı bir `public:` — okunabilirliği artırabilir; bu bir stil tercihidir, derleyicinin
davranışını değiştirmez.

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

Bu dersin ölçtüğü ifade **kendisi** sınıfındadır: `h.yatir(50)` `h`'nin kendisine yazar. Ama
erişim denetiminin sınırı da burada görülür: `private` bir üyeye "kimin eriştiği" nesne
düzeyinde değil sınıf düzeyinde sorulur. `esit_mi` içindeki `digeri.bakiye_` çağrısı bunu
kanıtlar — aynı sınıftan farklı bir nesnenin `private` üyesine, o nesnenin kendi işlevi
olmadan, yalnız aynı sınıfın bir başka üye işlevinden erişilebilir. `private`, nesneyi değil
sınıfın sınırını korur.

Bu sınırın bir sonucu daha vardır: `private` bir üyeye erişim, sınıfın **kendi** kaynak
kodundaki her üye işlevden mümkündür — bu üye işlevlerin sayısı sınıf büyüdükçe artabilir ve
her biri aynı değişmezi bozma riski taşır. `Hesap` üç üye işlevle küçüktür ve hepsi bu
derste görülebilir; büyük bir sınıfta `private` veriye erişen onlarca üye işlev olabilir, ve
bu derste ölçülen kural — erişimin nesneye değil sınıfa bağlı olması — o ölçekte de
değişmeden kalır. `private`, "kaç yerden erişilebilir" sorusuna nesne sayısıyla değil, sınıfın
üye işlev sayısıyla cevap verir.

Bu iki nokta — hangi ifadenin hangi nesneyle çalıştığı ve erişim denetiminin nesneyi değil
sınıfı sorduğu — birlikte okunduğunda bir sınıfın neden güvenilir bir sınır çizebildiğini
açıklar. `this` her çağrıda doğru nesneyi gösterir, bu yüzden bir üye işlev kendi nesnesini
başka bir nesneyle karıştırmaz; `private` her üye işleve aynı sınıftan gelen her nesneye
erişim tanır, bu yüzden sınıf kendi içinde veri paylaşımı gerektiren işlemleri (karşılaştırma,
kopyalama, birleştirme) yazabilir. Bu iki kural bir arada, bir sonraki dersin konusu olan
yapıcıya zemin hazırlar: bir yapıcı da bir üye işlevdir, kendi `this`'i vardır, ve `private`
veri üyelerine aynı serbestlikle erişir — ama onu farklı kılan, çağrıldığı nesnenin henüz
var olmayan bir nesne olmasıdır.

#### Özet

- Bu dersin ölçtüğü ifade **kendisi** sınıfındadır: `h.yatir(50)` çağrısında `this`, `&h`'ye
  eşittir.
- `class` ile `struct`'ın tek farkı varsayılan erişimdir; `public`/`private`/`protected`
  üç erişim düzeyini bildirir.
- `const` işaretli bir üye işlevde `this` de `const`'tur; veri üyeleri değiştirilemez.
- Erişim denetimi **derleme zamanında** ve **sınıf düzeyinde** çalışır: bir üye işlev, aynı
  sınıftan başka bir nesnenin `private` üyesine erişebilir.
- Kapsülleme M08'de tanımlandı; burada yalnız `class`/`private` sözdizimiyle uygulandı.

#### Sonraki Adım

Bu ders bir üye işlevin hangi nesneyle çalıştığını gösterdi; bir sonraki ders aynı soruyu
bir **yapıcı**ya sorar — `int`'ten örtük kurulan bir argümanın adsız, geçici bir
nesne olduğunu ve bu nesnenin tam olarak ne zaman yok olduğunu ölçer.

### Yapıcılar ve Yıkıcılar

Source: https://academia.sh/tr/kurslar/cpp-temelleri/yapicilar-ve-yikicilar

Bir int'ten örtük kurulan argümanın adsız, geçici bir nesne olduğunu ve yaşam süresini yapıcı/yıkıcı çiftiyle ölçer.

Önceki ders bir üye işlevin hangi nesneyle çalıştığını `this` üzerinden ölçtü — ama o nesne
her zaman zaten vardı. Bu ders aynı soruyu bir nesnenin **var olma anına** sorar: bir
**yapıcı** (constructor — sınıfın adıyla aynı adı taşıyan, nesne kurulurken bir kez çalışan
üye işlev) ne zaman çağrılır, ve bir **yıkıcı** (destructor — `~` önekli, nesne yok olurken
bir kez çalışan üye işlev) ne zaman çalışır? Ölçüm sınıfı bu kez **geçici**: bir işlev
çağrısının bir argümanından örtük olarak kurulan, adı olmayan bir nesnenin ömrünü
izleyecek.

#### Yapıcı ve Yıkıcı

Bir yapıcı, sınıfla aynı adı taşır, dönüş tipi yoktur (`void` bile yazılmaz) ve bir nesne
kurulurken derleyici tarafından çağrılır. Bir yıkıcı `~SinifAdi()` biçimindedir, bağımsız
değişken almaz, ve bir nesne yaşam süresinin sonuna geldiğinde derleyici tarafından çağrılır
— programcı onu elle çağırmaz. Bir sınıf hiç yapıcı yazmazsa derleyici **varsayılan yapıcı**
üretir (argüman almayan, veri üyelerini kendi varsayılan değerleriyle kuran); bir
yapıcı yazılırsa bu üretim durur.

Bir sınıfın birden çok yapıcısı olabilir — parametre sayısı ya da tipi farklı olduğu sürece
aynı işlev bindirimi (overload) kurallarıyla ayrılırlar. Buna karşılık bir sınıfın yalnız
**bir** yıkıcısı olabilir; yıkıcı argüman almadığı için bindirilemez.

Yapıcı ile sıradan bir üye işlev arasındaki en büyük fark, yapıcının çalıştığı anda nesnenin
**henüz tamamlanmamış** olmasıdır. `Tutar(int kurus)` çalışırken `kurus_` üye ilk değer
listesindeki `kurus_(kurus)` ile kurulur; gövde çalışmaya başladığında `kurus_` artık
geçerli bir değerdedir ve gövde içinde okunabilir, ama gövde çalışana kadar nesnenin diğer
üyeleri (bu sınıfta yalnız `kurus_` var, ama çok üyeli bir sınıfta sıradaki üyeler) henüz
kurulmamış olabilir. Bu yüzden bir yapıcının gövdesinde `this` üzerinden başka bir üye
işlevi çağırmak — özellikle sanal bir üye işlevi, bu kursun kapsamı dışında kalan bir konu
— nesnenin henüz tam kurulmamış bir durumuna erişme riski taşır; bu ders böyle bir çağrı
yapmaz.

Yıkıcının konumu simetriktir: yıkıcı çalıştığında nesne hâlâ tam ve geçerlidir, ama yıkıcı
döndükten sonra nesne artık yok sayılır — üye değişkenlerin belleği hâlâ durabilir ama
programın onlara erişmesi tanımsız davranıştır (bu kursun M11/K02'den devraldığı kural:
tanımsız davranış hiçbir zaman koşturulmaz, yalnız düzyazıda anılır). `Tutar::~Tutar()`
gövdesi `kurus_`'ü son kez okuyup basar; bu satırdan sonra hiçbir kod aynı nesnenin
`kurus_`'üne erişmemelidir.

```cpp
// tutar.hpp — bir yapici, bir yikici, bir de yapici zinciri
#ifndef TUTAR_HPP
#define TUTAR_HPP

#include <cstdio>

class Tutar {
public:
    Tutar(int kurus) : kurus_(kurus) {
        std::printf("yapici calisti kurus=%d\n", kurus_);
    }

    ~Tutar() {
        std::printf("yikici calisti kurus=%d\n", kurus_);
    }

    int kurus() const { return kurus_; }

private:
    int kurus_;
};

#endif
```

**SK5.** `Tutar(int kurus)` bir argüman alır ve gövdesinden önce çalışan `:
kurus_(kurus)` bir **üye ilk değer listesi**dir (member initializer list) — `kurus_` veri
üyesini gövde çalışmadan önce `kurus` değeriyle kurar. Gövde içinde `kurus_ = kurus` yazmak
da aynı sonucu verirdi, ama ilk değer listesi kurmayla atamayı ayırır: gövde çalıştığında
`kurus_` zaten vardır, yalnız değiştirilir.

#### Örtük Kurma ve `explicit`

`Tutar(int kurus)` **tek** bir `int` argüman alan bir yapıcıdır; C++ böyle bir
yapıcıyı **örtük tip dönüşümü** için de kullanır. `goster(Tutar t)` diye bir işlev
`goster(5)` ile çağrılırsa, derleyici `5`'i `Tutar`'a dönüştürmek için bu yapıcıyı örtük
olarak çağırır — kurulan nesnenin adı yoktur, `main`'de hiçbir değişken görünmez, ama nesne
gerçekten vardır ve yapıcısı gerçekten çalışır.

```cpp
// gecici-olcumu.cpp — int'ten ortuk kurulan bir tutar gecici midir
#include <cstdio>

#include "tutar.hpp"

static void goster(Tutar t) {
    std::printf("goster icinde kurus=%d\n", t.kurus());
}

int main() {
    std::printf("cagridan once\n");
    goster(5);
    std::printf("cagridan sonra\n");
    return 0;
}
```

```text
cagridan once
yapici calisti kurus=5
goster icinde kurus=5
yikici calisti kurus=5
cagridan sonra
```

**SK6.** `goster(5)` çağrısında `5` önce `Tutar(5)` ile bir `Tutar` nesnesine dönüştürülür —
bu nesnenin adı yoktur, bu yüzden **geçici**dir. Bu geçici, `goster`'in parametresi `t`'ye
kopyalanır (ya da doğrudan onun yerine kurulur — bu kursun kapsamı dışında kalan bir
derleyici optimizasyonu); `goster` döndüğünde `t`'nin yıkıcısı çağrılır ve çıktı sırası
bunu gösterir: yapıcı çağrıdan hemen sonra, yıkıcı `goster` döndükten hemen sonra, ama
`cagridan sonra` satırından **önce** çalışır — geçicinin ömrü `goster(5);` ifadesiyle
sınırlıdır, `main`'in geri kalanına taşmaz.

Bu örtük dönüşüm her zaman istenmez: `goster(5)` yazıldığında `5`'in bir `Tutar` olarak mı
yoksa yanlışlıkla bir tamsayı olarak mı geçirildiği kod okuyucusuna açık değildir. `explicit`
anahtar sözcüğü bunu engeller: `explicit Tutar(int kurus)` yazılsaydı, `goster(5)` derlenmez,
yalnız `goster(Tutar(5))` ya da `goster(Tutar{5})` gibi açık bir kurma derlenirdi. Bu ders
`Tutar`'ı `explicit` yazmadan bırakır çünkü ölçülmek istenen tam olarak örtük kurmanın
ürettiği geçicidir; ama bir sınıf yazarı tek-bağımsız-değişkenli her yapıcıyı `explicit`
yapmayı düşünmelidir — bu kursun ilerideki derslerinde yazılan yapıcılar da aksi
belirtilmedikçe `explicit` olarak yazılır.

Örtük dönüşümün riski yalnız okunabilirlik değildir. İki argüman zinciri örtük
kurmaya izin verdiğinde — örneğin `Tutar` bir yapıcıdan başka bir sınıfa örtük dönüşüyorsa —
derleyici bir çağrıyı beklenmedik bir zincirle derleyebilir; C++ standardı bir çağrıda
**en fazla bir** örtük kullanıcı tanımlı dönüşüme izin vererek bunu sınırlar, ama tek
adımlık örtük dönüşüm bile kod okuyanın beklemediği bir yapıcı çağrısı gizleyebilir.
`goster(5)` örneğinde bu risk küçüktür çünkü `Tutar`'ın tek yapıcısı vardır ve niyet
açıktır; büyük bir sınıf hiyerarşisinde aynı örtük kurma, çağıranın fark etmediği bir
nesne kurma maliyetini (yapıcı çalışması, yıkıcı çalışması) sessizce ekleyebilir. `explicit`
bu sessizliği kaldırır: her kurma kaynak kodda görünür bir `Tür(...)` çağrısı olarak
görünmek zorunda kalır.

#### Yapıcı Zinciri ve Ters Sıra Yıkım

Birden çok veri üyesi olan bir sınıfta üye ilk değer listesi, üyelerin **bildirim
sırasıyla** kurulmasını sağlar — listede yazılma sırası değil, sınıf gövdesindeki bildirim
sırası geçerlidir. Yıkım bunun tam tersi sırada olur: en son kurulan üye en önce yıkılır.

```cpp
// ciftlik.cpp — birden fazla uyenin kurulma ve yikilma sirasi
#include <cstdio>

#include "tutar.hpp"

class Ciftlik {
public:
    Ciftlik() : birinci_(1), ikinci_(2) {
        std::printf("ciftlik yapicisi calisti\n");
    }

private:
    Tutar birinci_;
    Tutar ikinci_;
};

int main() {
    std::printf("blok basladi\n");
    {
        Ciftlik c;
        std::printf("ciftlik kuruldu\n");
    }
    std::printf("blok bitti\n");
    return 0;
}
```

```text
blok basladi
yapici calisti kurus=1
yapici calisti kurus=2
ciftlik yapicisi calisti
ciftlik kuruldu
yikici calisti kurus=2
yikici calisti kurus=1
blok bitti
```

**SK7.** `Ciftlik`'in kendi yapıcı gövdesi çalışmadan önce `birinci_` ve `ikinci_`, sınıf
gövdesindeki bildirim sırasıyla (üye ilk değer listesindeki sıra değil — burada ikisi aynı
olduğu için fark görünmüyor, ama sıra her zaman bildirime göre belirlenir) kurulur; her
kurma kendi `Tutar` yapıcısını tetikler, bu yüzden `ciftlik yapicisi calisti` satırından
önce iki ayrı `yapici calisti` satırı görülür. `c` iç blok kapanırken yok olduğunda önce
`Ciftlik`'in kendi yıkıcısı (burada yazılmadığı için derleyici üretir, hiçbir çıktı basmaz),
sonra üyeleri **ters sırada** yıkılır: `ikinci_` önce, `birinci_` sonra. Kurma ile yıkım
birbirinin aynasıdır.

M10/K02'de (Rust) bir değerin kapsam dışına çıkışı `Drop` çalıştırıyordu; C++'ın yıkıcısı
aynı fikri karşılar, ama tetiklenme kuralları farklıdır ve bu ders o karşılaştırmayı
derinleştirmez.

Bildirim sırasının bağlayıcı olması, üye ilk değer listesinin **yazılma** sırasını görmezden
gelmesi anlamına gelir — bazı derleyiciler bir uyarı olarak listedeki sıranın bildirim
sırasıyla eşleşmediğini bildirir, çünkü bu iki sıranın farklı olması okuyucuyu yanıltabilir:
liste `ikinci_(2), birinci_(1)` sırasıyla yazılsa bile kurulma sırası yine `birinci_` önce
olurdu, çünkü sınıf gövdesinde `birinci_` önce bildirilmiştir. `Ciftlik` bu ders için listeyi
bildirim sırasıyla aynı yazar, bu yüzden iki sıra çakışır ve karışıklık doğmaz — ama kural
her zaman bildirim sırasıdır, listenin yazılış sırası değil.

Ters sıra yıkımın bir başka sonucu, bir üyenin yapıcısı bir öncekinin kurduğu bir kaynağa
güvenle kullanabilmesidir: `ikinci_` kurulurken `birinci_` zaten tam kurulmuştur, çünkü
bildirim sırasında ondan öncedir. Aynı güvence yıkımda tersine işler: `ikinci_` yıkılırken
`birinci_` henüz yıkılmamıştır, bu yüzden `ikinci_`'nin yıkıcısı gerekirse `birinci_`'ye
hâlâ güvenle erişebilir. Bu simetri, bir sonraki dersin konusu olan RAII'nin (Resource
Acquisition Is Initialization) üzerine kurulduğu temeldir: bir kaynağı tutan üye, onu
tutan nesnenin dışına çıkmadan önce yıkılmaz.

#### Yapıcıların Birbirini Çağırması

Bir sınıfın birden çok yapıcısı olduğunda, bu yapıcılar arasında kod tekrarını önlemek için
bir yapıcı diğerini **devrederek** (delegating constructor) çağırabilir: `Tutar() :
Tutar(0) {}` yazmak, argümansız çağrıldığında işi tek-bağımsız-değişkenli yapıcıya
devreden bir yapıcı tanımlar. Devir, üye ilk değer listesinde sınıfın kendi adını çağırarak
yazılır — `Tutar(0)` burada başka bir üyenin değil, sınıfın kendisinin bir yapıcısını
çağırır. Devredilen yapıcı tam olarak çalışır (kendi gövdesi dahil) ve devreden yapıcının
gövdesi ondan sonra çalışır. `Tutar` bu dersin kapsamında devir kullanmaz — tek yapıcısı
vardır — ama bu teknik, birden çok yapıcı arasında ortak kurma mantığını tek bir yerde
tutmanın standart yoludur; ilk değer listesinde üye kurmakla karıştırılmamalıdır, çünkü
biri sınıfın kendi adını, diğeri bir üyenin adını çağırır.

#### Varsayılan Yapıcı ve Varsayılan Değerler

`Tutar` argüman almayan bir yapıcı yazmaz, çünkü bir tutarın her zaman bir başlangıç
değeriyle kurulmasını ister — `Tutar t;` yazmak bu sınıf için derlenmez, çünkü tek yapıcı
bir `int` ister. Bir sınıf hiçbir yapıcı yazmazsa derleyici argüman almayan bir
**varsayılan yapıcı** üretir; bu üretilen yapıcı her veri üyesini kendi varsayılan değeriyle
kurar — sınıf tipindeki üyeler kendi varsayılan yapıcılarını çalıştırır, `int` gibi temel
tipler ise (üye bildiriminde bir ilk değer verilmemişse) **kurulmamış** kalır, yani rastgele
bir bellek değeri taşır. Bu, C'den gelen bir davranıştır ve C++'ta değişmemiştir: `Ciftlik`
gibi yalnız sınıf tipinde üyesi olan bir sınıfın varsayılan yapıcısı güvenlidir, çünkü her
üye kendi yapıcısını çalıştırır; ama `int miktar_;` gibi bir üye ilk değersiz bırakılırsa
varsayılan yapıcı onu kurulmamış bırakır. Bu kursta yazılan sınıflar, temel tipteki her
üyeye ya bildirimde (`int miktar_ = 0;`) ya da üye ilk değer listesinde açık bir başlangıç
değeri verir; kurulmamış bir üyeyi okumak da tanımsız davranıştır ve koşturulmaz.

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

Bu dersin ölçtüğü ifade — `goster(5)` içindeki örtük kurulan `Tutar` — **geçici**
sınıfındadır: adı yoktur, `goster(5);` ifadesinin sonunda yok olur. Ama geçicinin ömrü her
zaman bu kadar kısa kalmaz: bir geçici, `const` bir referansa bağlanırsa referansın ömrüne
uzar. `const Tutar& r = Tutar(7);` yazılsaydı, sağ taraftaki geçici normalde ifade
bitiminde yok olurdu, ama `r`'ye bağlandığı için ömrü `r`'nin kapsamının sonuna kadar
uzatılır — derleyici bunu otomatik yapar, programcının bir şey yazması gerekmez. Bu
uzatma yalnız `const` referanslar (ve bu kursun kapsamı dışındaki rvalue referanslar) için
geçerlidir; `Tutar& r = Tutar(7);` yazmak derlenmez, çünkü `const` olmayan bir referans bir
geçiciye bağlanamaz. Referansın kendisi bir başka geçiciye **yeniden** bağlanamaz olması —
bir referansın kurulduktan sonra hep aynı nesneyi göstermesi — bir sonraki konudaki
referanslar dersinin ölçtüğü şeydir.

Bu sınır, geçicilerin neden işlev çağrı zincirlerinde dikkatli kullanılması gerektiğini
açıklar: `goster(Tutar(5))` gibi bir çağrıda geçici yalnız `goster`'in çalıştığı ifade
boyunca yaşar; geçiciye ait bir işaretçi ya da `const` olmayan bir referans bu ifadeden
dışarı taşınırsa, ifade bittikten sonra o işaretçi ya da referans **sarkan** (dangling) hâle
gelir — gösterdiği bellek artık geçerli değildir. `Tutar`'ın `kurus()` işlevinin bir `int`
döndürmesi, `const Tutar&` ya da `Tutar*` değil, tam olarak bu riski taşımamak içindir:
`int` bir değer olarak kopyalanır, geçicinin kendisi yok olsa da kopyalanan değer geçerli
kalır.

Bu üç kural — örtük kurmanın geçici ürettiği, bildirim sırasının kurmayı ve yıkımı
belirlediği, `const` referansın geçicinin ömrünü uzatabildiği — bir arada bir nesnenin
"var olmak" ile "adlandırılmış olmak"ın aynı şey olmadığını gösterir. `goster(5)` içindeki
`Tutar` hiçbir değişkene atanmadan kurulur, yapıcısı çalışır, kullanılır, yıkıcısı çalışır;
bu tüm süreç boyunca hiçbir isim ona işaret etmez. Bir sonraki dersin RAII'si tam olarak bu
gözlemi kullanır: bir kaynağı serbest bırakmak için bir nesnenin adlandırılmış olması
gerekmez, yalnız yaşam süresinin sonuna gelmesi yeterlidir — derleyici o anı, adı olsun ya
da olmasın, her zaman bilir.

#### Özet

- Bu dersin ölçtüğü ifade **geçici** sınıfındadır: `goster(5)` içinde örtük kurulan `Tutar`
  adsızdır ve `goster(5);` ifadesinin sonunda yok olur.
- Bir yapıcı sınıfla aynı adı taşır, bir yıkıcı `~` önekiyle yazılır; yıkıcı bağımsız
  değişken almaz ve bindirilemez.
- Tek argümanlı bir yapıcı örtük tip dönüşümüne izin verir; `explicit` bunu
  engelleyip yalnız açık kurmaya izin verir.
- Üye ilk değer listesi üyeleri **bildirim sırasıyla** kurar; yıkım bunun **tersi**
  sırada olur.
- `const` bir referansa bağlanan bir geçicinin ömrü, referansın kapsamına kadar uzatılır.

#### Sonraki Adım

Bu ders bir nesnenin ne zaman kurulduğunu ve yok olduğunu ölçtü; bir sonraki ders bu
bilgiyi bir kaynağı (bellek, dosya tanıtıcısı gibi) güvenle serbest bırakmak için kullanır
— yıkıcının bir üyeyi nasıl bıraktığını, koruyucunun kendisiyle içindeki üyeyi ayırarak
ölçer.

### RAII

Source: https://academia.sh/tr/kurslar/cpp-temelleri/raii

Bir koruyucunun aldığı ve bıraktığı üyenin, koruyucu nesnenin kendisi değil içindeki bir parça olduğunu ölçer.

Önceki ders bir nesnenin ne zaman kurulduğunu ve yok olduğunu, adı olsun ya da olmasın,
ölçtü. Bu ders o bilgiyi bir amaç için kullanır: bir kaynağı — burada basit bir sayaç
kaynağı, gerçek programlarda bellek, dosya tanıtıcısı ya da kilit olabilir — güvenle serbest
bırakmak. Ölçüm sınıfı bu kez **parça**: bir koruyucunun aldığı ve bıraktığı şey, koruyucunun
kendisi değil, içindeki bir veri üyesidir.

#### RAII Tanımı

**RAII** (Resource Acquisition Is Initialization — kaynak edinimi ilk değerlemedir), bir
kaynağın bir nesnenin yapıcısında alınıp yıkıcısında bırakılmasını öngören bir tasarım
kalıbıdır. Kaynağı tutan nesne yaşadığı sürece kaynak da geçerlidir; nesne — adlandırılmış
ya da geçici, önceki derste görüldüğü gibi — herhangi bir yoldan yaşam süresinin sonuna
geldiğinde yıkıcı çalışır ve kaynağı bırakır. Bu, kaynağı elle bırakmayı (`serbest_birak()`
gibi bir işlevi her çağıran kodda tek tek çağırmayı) gereksiz kılar: kaynağı tutan nesne
hangi yoldan yok olursa olsun (işlevin sonuna gelme, erken `return`, istisna fırlatma),
yıkıcı **her zaman** çalışır.

RAII'nin karşıtı, kaynağı bir yapıcı/yıkıcı çiftine değil, çağıranın disiplinine bırakmaktır:
bir `kaynak_ac()` işlevi çağrılır, kullanılır, ve kodun her çıkış noktasında elle bir
`kaynak_kapat()` çağrılması beklenir. Bu yaklaşımın kırılganlığı, kod büyüdükçe ortaya çıkar
— bir işleve yeni bir erken `return` eklendiğinde, o `return`'den önce `kaynak_kapat()`
çağrısının da eklenmesi gerekir; eklenmezse kaynak sızar (leak — kaynağın hiç bırakılmadan
programın geri kalanında geçerli kalmaya devam etmesi). RAII bu riski, bırakma kararını
kaynağı tutan kodun değil, kaynağı **tutan nesnenin türünün** sorumluluğuna vererek ortadan
kaldırır: `Koruyucu`'yu kullanan kod hiçbir zaman bir bırakma çağrısı yazmaz, çünkü bu
çağrı zaten `Kaynak::~Kaynak()` içinde, bir kez, sınıfın kendisinde yazılıdır. Bu dersin ilk
bölümünde iki sınıf tanımlanır: kaynağı gerçekten tutan `Kaynak`, ve onu bir üye olarak saran
`Koruyucu`.

```cpp
// kaynak.hpp — sayilarla temsil edilen basit bir kaynak
#ifndef KAYNAK_HPP
#define KAYNAK_HPP

#include <cstdio>

class Kaynak {
public:
    explicit Kaynak(int kimlik) : kimlik_(kimlik) {
        std::printf("kaynak alindi kimlik=%d\n", kimlik_);
    }

    ~Kaynak() {
        std::printf("kaynak birakildi kimlik=%d\n", kimlik_);
    }

    int kimlik() const { return kimlik_; }

private:
    int kimlik_;
};

#endif
```

```cpp
// koruyucu.hpp — kaynagi tutan uyeyi alan ve birakan koruyucu
#ifndef KORUYUCU_HPP
#define KORUYUCU_HPP

#include "kaynak.hpp"

class Koruyucu {
public:
    explicit Koruyucu(int kimlik) : kaynak_(kimlik) {}

    int kimlik() const { return kaynak_.kimlik(); }

private:
    Kaynak kaynak_;
};

#endif
```

#### Koruyucunun Bıraktığı, Kendisi Değil İçindeki Üyedir

`Koruyucu`'nun kendi bir yapıcısı ve derleyicinin ürettiği bir yıkıcısı vardır; `Koruyucu`
`kaynak_` üyesini kendi kurduğunda `Kaynak` yapıcısı çalışır, kendi yok olduğunda `kaynak_`
yıkılır ve `Kaynak` yıkıcısı çalışır. Buradaki soru: koruyucunun aldığı ve bıraktığı şey,
`Koruyucu` nesnesinin kendisi mi, yoksa içindeki `kaynak_` üyesi mi?

```cpp
// parca-olcumu.cpp — koruyucunun tuttugu kaynak koruyucunun kendisi mi ici mi
#include <cstdio>

#include "koruyucu.hpp"

int main() {
    std::printf("blok basladi\n");
    {
        Koruyucu k(7);
        std::printf("kimlik %d\n", k.kimlik());
    }
    std::printf("blok bitti\n");
    return 0;
}
```

```text
blok basladi
kaynak alindi kimlik=7
kimlik 7
kaynak birakildi kimlik=7
blok bitti
```

**SK8.** `Kaynak`'ın yapıcısı `Koruyucu k(7)` satırında, `Koruyucu`'nun kendi yapıcı gövdesi
çalışmadan önce, üye ilk değer listesindeki `kaynak_(kimlik)` ile tetiklenir — `kaynak
alindi` satırı bu yüzden `blok basladi`'dan hemen sonra, `Koruyucu`'nun kendi gövdesi hiçbir
şey basmadan önce görülür. Kaynağı gerçekte alan `Kaynak` nesnesidir, ve bu nesne
`Koruyucu`'nun **kendisi değil**, onun bir üyesidir — `kaynak_`, `Koruyucu`'nun bellek
aralığının içinde bir alt nesnedir (subobject), ayrı, bağımsız bir nesne değildir. Bunun
kanıtı sıralamadadır: `k` blok sonunda yok olurken önce `Koruyucu`'nun (derleyicinin
ürettiği, hiçbir şey basmayan) yıkıcısı çalışır, ardından `Koruyucu`'nun üyesi olduğu için
`kaynak_`'ın yıkıcısı çalışır — `kaynak birakildi` satırı bu yüzden `blok bitti`'den önce
görülür. Eğer `kaynak_` `Koruyucu`'dan bağımsız, ayrı bir nesne olsaydı, onun yıkımı
`Koruyucu`'nun yıkımına bağlı olmazdı; oysa burada ikisi aynı anda, aynı kapsamın
kapanmasıyla tetiklenir — çünkü biri diğerinin parçasıdır.

Burada dikkat edilmesi gereken bir isim çakışması yoktur: `Kaynak` ile `Koruyucu`'nun ikisi
de kendi başlarına birer sınıftır, biri diğerini üye olarak barındırır. `Koruyucu`'nun
kendi yapıcısı `explicit Koruyucu(int kimlik) : kaynak_(kimlik) {}` gövdesizdir — tüm işi
üye ilk değer listesi yapar, `kaynak_`'ı `kimlik` ile kurar. `Koruyucu`'nun kendi bir
yıkıcısı yazılmamıştır; derleyici bir tane üretir, ve bu üretilen yıkıcı hiçbir şey
basmadan yalnız `kaynak_`'ın yıkıcısını tetikler — bu, önceki dersteki `Ciftlik` örneğinde
görülen aynı davranıştır: bir sınıf kendi yıkıcısını yazmasa bile, üyelerinin yıkıcıları
yine de çalışır.

#### Erken Çıkış Yollarında Yıkıcı Yine Çalışır

RAII'nin asıl değeri, kaynağı bırakan kodu tek bir yere (yıkıcıya) yazıp, o kodun **her**
çıkış yolunda çalışmasını derleyiciye bırakmaktır — işlevin normal sonu, erken `return`,
ya da bir istisna fırlatıldığında bile.

```cpp
// erken-cikis.cpp — koruyucu erken return yolunda da birakiyor mu
#include <cstdio>

#include "koruyucu.hpp"

static void calistir(bool erkenCik) {
    Koruyucu k(9);
    std::printf("calistir basladi kimlik=%d\n", k.kimlik());
    if (erkenCik) {
        std::printf("erken cikiliyor\n");
        return;
    }
    std::printf("normal bitiyor\n");
}

int main() {
    calistir(true);
    std::printf("main devam ediyor\n");
    return 0;
}
```

```text
kaynak alindi kimlik=9
calistir basladi kimlik=9
erken cikiliyor
kaynak birakildi kimlik=9
main devam ediyor
```

**SK9.** `calistir(true)` `if` bloğu içindeki `return`'e ulaştığında, `k`'nin kapsamı
(işlev gövdesi) sona erer ve derleyici `k`'nin yıkıcısını **`return`'den hemen önce**
çalıştırır — `kaynak birakildi` satırı `erken cikiliyor`'dan sonra, ama `main devam
ediyor`'dan önce görülür. Bu davranış `if` dalına bakılmaksızın aynıdır: `calistir(false)`
çağrılsaydı `normal bitiyor` satırından sonra yine aynı yıkıcı çalışırdı. Kaynağı bırakan kod
yalnız `Kaynak::~Kaynak()` içinde bir kez yazılıdır; `calistir`'in kendisi hiçbir bırakma
çağrısı yapmaz.

Bu güvence yalnız `if`/`return` gibi düzenli kontrol akışı için değil, C++'ın istisna
mekanizması için de geçerlidir: bir işlev gövdesinde bir istisna fırlatılırsa (`throw`),
yığıt o istisna bir `catch` bloğu tarafından yakalanana kadar **çözülür**
(yığıt çözülmesi) — bu çözülme sırasında, çözülen her kapsamdaki yerel nesnelerin
yıkıcıları, tıpkı normal bir `return`'de olduğu gibi sırayla çalıştırılır. `calistir`
içinde `k`'den sonra bir `throw` satırı olsaydı, `k`'nin yıkıcısı yine de istisna
`calistir`'in dışına çıkmadan önce çalışırdı. Bu kurs istisna fırlatma ve yakalamayı
öğretmez — o başka bir konudur — ama RAII'nin gerçek gücü tam olarak buradan gelir: kaynak
bırakma kodu, kontrol akışının **hangi yoldan** çıktığını bilmek zorunda değildir, çünkü
yıkıcı her yoldan çalışır.

#### Kaynağı Referansla Tutmanın Riski

RAII'nin sağlamlığı, koruyucunun kaynağı **değerle** (bir üye olarak, kendi ömrüyle bağlı)
tutmasına dayanır. Bir koruyucu kaynağı referansla ya da işaretçiyle tutarsa, koruyucunun
kendi ömrü ile kaynağın gerçek sahibinin ömrü ayrışabilir — koruyucu hâlâ yaşarken kaynak
zaten yok olmuş olabilir. `Koruyucu` bu riski taşımaz çünkü `kaynak_` bir referans ya da
işaretçi değil, doğrudan bir `Kaynak` değeridir; `Koruyucu` yaşadığı sürece `kaynak_` de
yaşar, çünkü aynı nesnenin bir parçasıdır. Bir koruyucu kaynağı değerle değil de bir
işaretçiyle tutacak şekilde yanlış tasarlansaydı — `Kaynak* kaynak_;` gibi — koruyucunun
kopyalanması yalnız işaretçiyi kopyalardı, gerçek `Kaynak` nesnesini değil; iki koruyucu aynı
kaynağı işaret eder, biri yıkıcısında kaynağı bırakınca diğeri artık geçersiz bir kaynağa
işaret eder hâle gelirdi. Bu ders bu hatayı koşturmaz — yalnız düzyazıda anılır — çünkü
kaynağın iki kez bırakılması tanımsız davranıştır.

Bu paylaşım sorununu standart kütüphanenin hazır RAII sarmalayıcılarıyla çözmek K05'in
konusudur; bu kurs kaynağı kendi yazdığı sınıflarla yönetir. M10/K02'nin (Rust) sahiplik
sistemi ve M11/K02'nin kaynak yönetimi aynı fikrin — bir kaynağın tam olarak bir sahibi
olması ve sahibin ömrü bittiğinde kaynağın otomatik bırakılması — farklı dillerdeki
karşılıklarıdır; bu ders o karşılaştırmayı derinleştirmez.

`Koruyucu`'nun kendi tasarımı bu riskten nasıl kaçındığını daha ayrıntılı görmek
faydalıdır. `kaynak_` bir `Kaynak` **değeridir**, bir `Kaynak*` ya da `Kaynak&` değildir;
bu yüzden `Koruyucu` nesnesinin bellek düzeninde `kaynak_`'ın kendisi (yalnız ona işaret
eden bir adres değil) yer alır. Bir `Koruyucu` kopyalandığında — bu kursun bir sonraki
dersinin konusu — `kaynak_` de kopyalanır, çünkü o da `Koruyucu`'nun bir parçasıdır; iki
`Koruyucu` iki ayrı `Kaynak` taşır, aynı kaynağı paylaşmazlar. Bir işaretçiyle tutulan bir
kaynakta bu doğru olmaz: işaretçinin kopyalanması yalnız adresi kopyalar, gösterilen nesneyi
değil, bu yüzden iki koruyucu aynı kaynağa işaret etmeye başlar. Değerle tutmanın bu
sonucu — kopyalamanın kaynağı da beraberinde kopyalaması — RAII'nin güvenilirliğinin bir
parçasıdır ve bir sonraki derste ayrıntılı ölçülür.

Bir kaynağı değerle tutmanın bir sınırı da vardır: kaynak, tutan nesnenin bellek düzeninde
yer aldığı için, kaynağın boyutu (ya da en azından kaynağı temsil eden tanıtıcının boyutu)
derleme zamanında bilinmelidir. `Kaynak` burada yalnız bir `int` taşıdığı için bu sorun
görünmez; gerçek bir kaynak — açık bir dosya, bir ağ bağlantısı — genellikle işletim
sisteminin verdiği sabit boyutlu bir tanıtıcı (handle, örneğin bir tamsayı ya da bir
işaretçi) ile temsil edilir, bu yüzden aynı değerle-tutma stratejisi gerçek kaynaklar için
de çalışır.

#### Çoklu Kaynak Tutan Bir Koruyucu

Bir koruyucunun tek bir kaynak yerine birden çok kaynak tutması da aynı kuralı izler: her
kaynak, koruyucunun bir başka üyesi olur, ve her üyenin kurulma/yıkılma sırası önceki
derste ölçülen bildirim sırası kuralına uyar. İki `Kaynak` üyesi olan bir `CiftKoruyucu`
tasarlansaydı, üyeler bildirim sırasıyla kurulur, ters sırada yıkılırdı — tıpkı `Ciftlik`
örneğindeki `birinci_`/`ikinci_` gibi. Bu ders `CiftKoruyucu`'yu koşturmaz, çünkü tek bir
kaynağın alınıp bırakılması RAII'nin çekirdek fikrini göstermek için yeterlidir; ama bu
genellemenin doğrudan önceki dersten geldiğini görmek önemlidir — RAII yeni bir dil kuralı
eklemez, yalnız yapıcı/yıkıcı sırasını bir amaç için kullanır.

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

Bu dersin ölçtüğü ifade **parça** sınıfındadır: `kaynak_`, `Koruyucu`'nun kendisi değil,
onun içindeki bir alt nesnedir, ve `Koruyucu`'nun yaşam süresiyle aynı yaşam süresine
sahiptir. Ama RAII'nin güvencesi koruyucunun **kaynağı değerle tutmasına** bağlıdır — bir
koruyucu kaynağa yalnız bir referans tutuyorsa (kaynağın gerçek sahibi başka bir nesneyse),
koruyucu korunan nesneden **uzun yaşamamalıdır**; koruyucu hâlâ yaşarken korunan nesne yok
olursa, koruyucunun referansı sarkan hâle gelir ve koruyucunun yıkıcısı (varsa) geçersiz bir
nesneye erişmeye çalışabilir. Bu ders bu riski koşturmaz, çünkü koşturmak tanımsız davranışı
tetikler; `Koruyucu` burada kaynağı her zaman değerle tuttuğu için bu risk zaten yoktur.

Bu sınırın pratik bir sonucu, bir koruyucu tasarlarken "bu kaynağın gerçek sahibi kim?"
sorusunun her zaman açıkça yanıtlanması gerektiğidir. `Koruyucu`'da yanıt nettir: `Koruyucu`
kendisi sahiptir, çünkü `kaynak_` onun bir değer üyesidir. Bir koruyucu bunun yerine
"başka bir yerde yaşayan bir kaynağı yalnız gözlemliyorum" demek isteseydi — örneğin bir
işlevin parametresinde alınan bir `Kaynak&`'i saklamak isteseydi — bu artık RAII değil,
yalnız bir referans tutmak olurdu, ve kaynağı bırakma sorumluluğu koruyucuda olmazdı. İki
tasarım da C++'ta geçerlidir, ama birbirine karıştırılmamalıdır: bir sınıfın yıkıcısı
yalnız **kendi sahip olduğu** kaynakları bırakmalıdır, ödünç aldığı bir kaynağı değil.

Bu ders boyunca ölçülen üç örnek — kurulma sırasının kanıtı, erken çıkışta yıkıcının
çalışması, kaynağın değerle tutulması — birlikte RAII'nin neden bir "en iyi uygulama"dan
fazlası olduğunu gösterir: bu, dilin yapıcı/yıkıcı ve bildirim sırası kurallarının doğrudan
bir sonucudur, ayrı bir mekanizma değildir. Bir sonraki dersin kopyalama sorusu da aynı
temelin üstüne kurulur — bir `Koruyucu` kopyalandığında `kaynak_` de kopyalanacaktır, ama
bu kopyanın "derin" mi "sığ" mı olduğu, kaynağın türüne bağlı olarak farklı sonuçlar
doğurabilir.

#### Özet

- Bu dersin ölçtüğü ifade **parça** sınıfındadır: `kaynak_`, `Koruyucu` nesnesinin
  kendisi değil, onun bir alt nesnesidir.
- RAII bir kaynağı yapıcıda alır, yıkıcıda bırakır; kaynağı elle bırakan kod gerekmez.
- Yıkıcı, kapsamın nasıl sona erdiğine bakılmaksızın çalışır — normal bitiş, erken
  `return`, ya da istisna fırlatma, hepsinde aynıdır.
- Kaynağı **değerle** tutan bir koruyucu, koruyucunun ömrüyle kaynağın ömrünü birleştirir;
  referansla tutan bir koruyucu bu güvenceyi kaybeder.
- Akıllı işaretçiler RAII'yi paylaşılan kaynaklara genişletir; bu kursun kapsamı dışındadır
  (K05).

#### Sonraki Adım

Bu ders bir kaynağın yapıcı/yıkıcı çiftiyle nasıl güvenle tutulduğunu ölçtü; bir sonraki
ders bir nesne **kopyalandığında** bu güvencenin ne olduğunu sorar — kopyalayan yapıcının
kendi kaynağını mı aldığını, yoksa orijinaliyle paylaştığını mı ölçer.

### Kopyalama ve Atama

Source: https://academia.sh/tr/kurslar/cpp-temelleri/kopyalama-ve-atama

Kopyalayan yapıcı ile kopyalayan atamanın kendi dizisini alan derin bir kopya ürettiğini, ve kopyanın derinliğinin sınıfın tasarımına bağlı olduğunu ölçer.

Önceki ders bir koruyucunun kaynağı değerle tuttuğunu, bu yüzden koruyucu kopyalandığında
kaynağın da kopyalanacağını öne sürdü. Bu ders o iddiayı ölçer: bir nesne kopyalandığında,
kopya orijinaliyle aynı belleği mi paylaşır, yoksa kendi belleğini mi alır? Ölçüm sınıfı
**kopya**: `Tampon b(a)` ile kurulan `b`, `a`'dan yeni kurulmuş, `a`'dan farklı bir nesnedir
— ama "farklı" olmanın derinliği, sınıfın kopyalayan yapıcısının nasıl yazıldığına bağlıdır.

C'de bir dizi kopyalamak istendiğinde bu her zaman **açık** bir işlemdi: `memcpy` çağrılır,
hedefin yeterince büyük olduğu elle denetlenir, kaynak ve hedefin çakışmadığı elle
garanti edilir. C++'ta bir sınıf nesnesi `=` ile ya da bir argüman olarak
değerle aktarıldığında kopyalama **örtük**tir — derleyici hangi işlevin çağrılacağına
kendisi karar verir, elle bir çağrı yazılmaz. Bu, kopyalamayı kolaylaştırır ama aynı
zamanda gizler: bir satırda `Tampon b(a);` görüp bunun ne kadar iş yaptığını (yeni bellek
ayırma, eleman eleman kopyalama) anlamak için sınıfın tanımına bakmak gerekir — satırın
kendisi bunu söylemez.

#### Kopyalayan Yapıcı ile Kopyalayan Atama

Bir sınıfın **kopyalayan yapıcısı** (copy constructor), aynı sınıftan bir `const&` alan bir
yapıcıdır — `Tampon(const Tampon& digeri)`. Bu yapıcı, `Tampon b(a);` ya da `Tampon b = a;`
gibi bir **kurma** sırasında çağrılır: `b` henüz yoktur, `a`'dan kurulur. **Kopyalayan
atama** (copy assignment) ise `operator=` bindirimidir — `Tampon& operator=(const Tampon&
digeri)`. Bu, `b = a;` gibi bir **atama** sırasında çağrılır: `b` zaten vardır (daha önce
kurulmuştur), yalnız değeri `a`'nınkiyle değiştirilir. İkisi de argüman tipi
olarak `const Tampon&` alır, ama biri yeni bir nesne kurar, diğeri var olan bir nesnenin
içeriğini değiştirir — bu fark, hangisinin çağrılacağını belirler: sol taraftaki nesne yeni
mi, var olan mı.

Bu ayrım yalnız sözdizimsel değildir. Kopyalayan yapıcı çalıştığında nesnenin veri üyeleri
henüz **kurulmamıştır** — üçüncü dersteki üye ilklendirme listesiyle aynı kural burada da
geçerlidir: `veri_(new int[digeri.uzunluk_])` üyeyi ilk kez ilklendirir. Kopyalayan atama
çalıştığında ise nesne zaten kurulmuş, `veri_` zaten geçerli bir diziye işaret ediyordur —
bu yüzden `operator=` önce eski diziyi bırakmak (`delete[] veri_`) **zorundadır**; aksi
hâlde eski dizi hiç serbest bırakılmadan üzerine yeni bir tane atanır, bu da bir bellek
sızıntısıdır (memory leak). Kopyalayan yapıcının böyle bir eski durumu yoktur, çünkü nesne
henüz yoktur — bu yüzden kopyalayan yapıcı `delete[]` çağırmaz, yalnız ayırır.

```cpp
// tampon.hpp — kendi dizisini tutan, derin kopyalayan bir sinif
#ifndef TAMPON_HPP
#define TAMPON_HPP

#include <cstdio>

class Tampon {
public:
    explicit Tampon(int uzunluk) : uzunluk_(uzunluk), veri_(new int[uzunluk]) {
        for (int i = 0; i < uzunluk_; ++i) {
            veri_[i] = i;
        }
        std::printf("yapici calisti uzunluk=%d\n", uzunluk_);
    }

    Tampon(const Tampon& digeri) : uzunluk_(digeri.uzunluk_), veri_(new int[digeri.uzunluk_]) {
        for (int i = 0; i < uzunluk_; ++i) {
            veri_[i] = digeri.veri_[i];
        }
        std::printf("kopyalayan yapici calisti uzunluk=%d\n", uzunluk_);
    }

    Tampon& operator=(const Tampon& digeri) {
        if (this == &digeri) {
            return *this;
        }
        delete[] veri_;
        uzunluk_ = digeri.uzunluk_;
        veri_ = new int[uzunluk_];
        for (int i = 0; i < uzunluk_; ++i) {
            veri_[i] = digeri.veri_[i];
        }
        std::printf("kopyalayan atama calisti uzunluk=%d\n", uzunluk_);
        return *this;
    }

    ~Tampon() {
        delete[] veri_;
    }

    int ilkEleman() const { return veri_[0]; }
    void ilkElemaniYaz(int deger) { veri_[0] = deger; }
    const int* adres() const { return veri_; }

private:
    int uzunluk_;
    int* veri_;
};

#endif
```

#### Derin Kopya: Kendi Dizisini Alır

```cpp
// kopya-olcumu.cpp — b, a'dan yeni kurulmus, kendi dizisini alan bir kopya mi
#include <cstdio>

#include "tampon.hpp"

int main() {
    Tampon a(3);
    Tampon b(a);

    std::printf("a ile b ayni dizi mi %s\n", a.adres() == b.adres() ? "evet" : "hayir");

    b.ilkElemaniYaz(99);
    std::printf("a ilk eleman %d\n", a.ilkEleman());
    std::printf("b ilk eleman %d\n", b.ilkEleman());
    return 0;
}
```

```text
yapici calisti uzunluk=3
kopyalayan yapici calisti uzunluk=3
a ile b ayni dizi mi hayir
a ilk eleman 0
b ilk eleman 99
```

**SK10.** `Tampon b(a);` `b`'yi `a`'dan kopyalayan yapıcıyla kurar; kopyalayan yapıcı
`digeri.uzunluk_`'i okuyup **kendi** `new int[digeri.uzunluk_]` çağrısıyla ayrı bir dizi
ayırır, sonra `digeri.veri_`'nin elemanlarını bu yeni diziye tek tek kopyalar. Bu bir **derin
kopya**dır (deep copy): `b.veri_` ile `a.veri_` farklı adreslerdir, `a.adres() == b.adres()`
`hayir` basar. Bu yüzden `b.ilkElemaniYaz(99)` yalnız `b`'nin kendi dizisini değiştirir;
`a.ilkEleman()` hâlâ `0`'dır. `b`, `a`'dan **kopya** sınıfındadır: yeni kurulmuş, `a`'dan
adres olarak farklı, ama kurulduğu andaki değeriyle eşit bir nesnedir.

#### Kopyalayan Atama ve Kendine Atama Denetimi

```cpp
// atama-olcumu.cpp — b = a atamasi kopyalayan atamayi calistirir mi
#include <cstdio>

#include "tampon.hpp"

int main() {
    Tampon a(2);
    Tampon b(5);

    b.ilkElemaniYaz(-1);
    a.ilkElemaniYaz(42);
    b = a;
    std::printf("atamadan sonra b'nin ilk elemani a'ninkiyle ayni mi %s\n",
                b.ilkEleman() == a.ilkEleman() ? "evet" : "hayir");
    std::printf("a ile b ayni dizi mi %s\n", a.adres() == b.adres() ? "evet" : "hayir");

    b = b;
    std::printf("kendine atamadan sonra b gecerli mi %s\n", b.ilkEleman() == a.ilkEleman() ? "evet" : "hayir");
    return 0;
}
```

```text
yapici calisti uzunluk=2
yapici calisti uzunluk=5
kopyalayan atama calisti uzunluk=2
atamadan sonra b'nin ilk elemani a'ninkiyle ayni mi evet
a ile b ayni dizi mi hayir
kendine atamadan sonra b gecerli mi evet
```

**SK11.** `b = a;` satırında `b` zaten kurulmuş bir nesnedir, bu yüzden kopyalayan yapıcı
değil kopyalayan atama çağrılır. `operator=` önce `b`'nin **eski** dizisini `delete[]
veri_` ile serbest bırakır, sonra `a`'nınkiyle aynı uzunlukta yeni bir dizi ayırıp
`a.veri_`'nin içeriğini kopyalar — bu yüzden atamadan sonra `b`'nin ilk elemanı `a`'nınkiyle
eşittir (içerik kopyalandı), ama `b.adres()` `a.adres()`'e eşit **değildir** (yeni bir dizi
ayrıldı, paylaşılmadı). `b`'nin atamadan önceki adresi burada ayrıca yazdırılıp
karşılaştırılmaz: `delete[]` ile hemen ardından gelen `new`'in aynı adresi geri verip
vermeyeceği ayırıcıya (allocator) bağlıdır, C++ standardı bunu garanti etmez — bu yüzden
böyle bir karşılaştırma kanıt sayılmaz, yalnız içerik ve `a` ile paylaşılmama durumu
ölçülür. `b = b;` satırı `operator=`'in başındaki `if (this == &digeri) return *this;`
denetimini tetikler: bu denetim olmasaydı, işlev önce `b`'nin kendi dizisini `delete[]`
ile bırakır, sonra `digeri.veri_`'yi (ki bu artık bırakılmış, geçersiz aynı adrestir)
okumaya çalışırdı — bu tanımsız davranış olurdu. Denetim sayesinde `b = b;` güvenle erken
döner, `b` değişmeden kalır.

#### Sığ Kopyanın Riski ve Üç Kuralı

`Tampon`'un kopyalayan yapıcısı ile kopyalayan ataması elle yazılmasaydı, derleyici kendi
ürettiği varsayılan versiyonları kullanırdı — bunlar her veri üyesini **üye üye** kopyalar.
`uzunluk_` bir `int` olduğu için bu sorunsuzdur, ama `veri_` bir `int*`'tir; üye üye
kopyalama işaretçinin kendisini kopyalar, gösterdiği diziyi değil. Bu bir **sığ kopya**dır
(shallow copy): iki `Tampon` aynı `veri_` adresini paylaşır. Sığ kopyanın sonucu şudur —
biri yıkılırken `delete[] veri_` çalıştırır, diğeri hâlâ aynı (artık bırakılmış) diziye
işaret ediyor olur; o nesne de yıkılırken aynı diziyi tekrar `delete[]` ile bırakmaya
çalışır. Bu **çift serbest bırakma** (double free) tanımsız davranıştır ve bu ders bunu
koşturmaz — yalnız düzyazıda anılır, `Tampon` her zaman elle yazılmış derin kopyalayan
yapıcı ve atamayla koşturulur.

Bu gözlem **üç kuralı**na (rule of three) götürür: bir sınıf yıkıcısını, kopyalayan
yapıcısını ya da kopyalayan atamasını elle yazmak zorundaysa (genellikle bir kaynağı ham
bir işaretçiyle tuttuğu için), üçünü de elle yazmalıdır — biri elle yazılıp diğer ikisi
derleyiciye bırakılırsa, sığ kopya riski doğar. `Tampon` bu kuralı tam uygular: yıkıcı,
kopyalayan yapıcı ve kopyalayan atama, üçü de `veri_`'yi doğru yönetir.

Kuralın "üç" ile sınırlı olmasının bir nedeni var: üçü de aynı sorunun (kaynağın sahipliği)
farklı yüzleridir. Yıkıcı kaynağı ne zaman bırakacağını, kopyalayan yapıcı kaynağın nasıl
çoğaltılacağını, kopyalayan atama ise var olan bir kaynağın nasıl değiştirileceğini
tanımlar. Biri unutulursa (örneğin yalnız yıkıcı yazılıp kopyalayan yapıcı derleyiciye
bırakılırsa), derleyicinin ürettiği sığ kopyalayan yapıcı `veri_`'yi paylaştırır — sonra iki
nesne aynı diziyi ayrı ayrı `delete[]` ile bırakmaya çalışır, bu da çift serbest bırakmadır.
Yıkıcının varlığı bile, geri kalan ikisinin de elle yazılması gerektiğinin bir işaretidir:
bir sınıf yıkıcısında özel bir şey yapıyorsa (yalnız `delete[]` gibi), bu genellikle o
sınıfın bir kaynağın **sahibi** olduğu anlamına gelir, ve sahiplik kopyalama sırasında da
düşünülmelidir.

#### `= delete` ile Kopyalamayı Engellemek

Her sınıfın kopyalanabilir olması gerekmez. Bir kaynağın tek bir sahibi olması isteniyorsa
— örneğin bir dosya tanıtıcısı (file handle) ya da bir ağ bağlantısı ikinci bir nesneyle
paylaşılmamalıysa — kopyalayan yapıcı ve kopyalayan atama `= delete` ile açıkça
**silinebilir**: `Tampon(const Tampon&) = delete;`. Bu, derleyiciye o işlevi hiç üretmemesini
değil, kullanıldığında bir derleme hatası vermesini söyler — `Tampon b(a);` yazmaya
çalışan kod derlenmez. `Tampon` bu dersin ölçümü için kopyalanabilir kalır, `= delete`
yalnız burada anılır; hangi sınıfların kopyalanamaz tasarlanması gerektiği ve bu kararın
sahiplik modeliyle ilişkisi K05'in konusudur.

#### Kopyalayan Yapıcının Argümanı Neden `const&`dir

Kopyalayan yapıcının argümanı `const Tampon&` olarak alınır, `Tampon` olarak
değil. Argüman değerle (`Tampon digeri`) alınsaydı, işlevi çağırmak için `digeri`
kendisi bir kopya gerektirirdi — bu kopyayı kuracak işlev de kopyalayan yapıcının kendisi
olurdu, sonsuz bir döngü. Referansla almak bu döngüyü kırar: hiçbir ek kopya üretilmeden
orijinal nesneye doğrudan erişilir. `const` ise `digeri`'nin yalnız okunacağını, kopyalama
sırasında değiştirilmeyeceğini garanti eder — kopyalayan yapıcı kaynağını değiştirmemelidir,
yalnız ondan okumalıdır.

#### Kopya, Kaynağın Derinliğini Söylemez

Önceki dersin `Koruyucu`'su gibi, `Tampon` da kaynağını değerle tutar — ama `Koruyucu`'nun
`kaynak_` üyesi bir `Kaynak` **değeri**yken, `Tampon`'un `veri_` üyesi bir `int*`'tir; kaynak
(dizi) öbek üzerindedir, üye yalnız ona bir işaretçidir. Bu ders `Tampon`'u derin
kopyalayacak şekilde **elle** yazarak bu farkı kapatır, ama kopyanın derinliği hiçbir zaman
otomatik değildir — sınıfın kopyalayan yapıcısının ne yazdığına bağlıdır. Bir sınıf ham bir
işaretçiyle paylaşılan bir kayda bağlıysa (örneğin `int*` yalnız başka bir nesnenin sahip
olduğu bir diziyi işaret ediyorsa, sahiplik `Tampon`'da değilse), üye üye kopyalama bu
işaretçiyi de kopyalar ve kopya da orijinaliyle aynı kaydı paylaşır — derin kopya yalnız
sınıf kendi kaynağının **sahibi** olduğunda ve kopyalayan yapıcı bunu açıkça yeniden
ayırdığında gerçekleşir.

Bu, aynı sınıfın farklı üyelerinin farklı kopyalama davranışları isteyebileceği anlamına
gelir. `Tampon`'un bir de, sahibi olmadığı bir kaynağa işaret eden `const int* referans_`
adında bir üyesi olsaydı, kopyalayan yapıcının bu üyeyi **kasıtlı** olarak sığ kopyalaması
— işaretçiyi olduğu gibi kopyalayıp yeni bellek ayırmaması — doğru olurdu, çünkü `Tampon`
o kaynağın sahibi değildir, yalnız ona bakar. Kopyalayan yapıcı yazarken sorulması gereken
soru şudur: bu üye bu nesnenin sahip olduğu bir kaynak mı, yoksa yalnız bakılan bir kaynak
mı? Cevap üyeden üyeye değişebilir, ve kopyalayan yapıcının gövdesi bu kararların toplamıdır
— derleyicinin ürettiği varsayılan üye üye kopyalama bu ayrımı hiç yapmaz, her üyeyi aynı
biçimde (sığ) kopyalar.

Taşıma (move) — bir nesnenin kaynağını kopyalamadan, kaynağı **devralarak** aktarması — bu
sorunu farklı bir yoldan çözer ve bu kursun kapsamı dışındadır (K05). M11/K01'de bir yapının
atamayla bütün alanlarıyla kopyalandığı ve bellek düzeni — üye sırası, hizalama ve
dolgu — işlenmişti; bu ders ikisini birleştirir: kopyalama hem değer semantiğine (kopya
orijinalden bağımsızdır) hem bellek yönetimine (kopya kendi belleğini alır) aynı anda
uyar.

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

Bu dersin ölçtüğü ifade **kopya** sınıfındadır: `b`, `a`'dan yeni kurulmuş, `a`'dan adres
olarak ayrı bir nesnedir. Ama kopya "ne kadar derin" olduğunu kendiliğinden söylemez — bu
bilgi kopyanın sonucunda değil, sınıfın kopyalayan yapıcısının kaynağıyla nasıl davrandığında
gizlidir. `Tampon` derin kopyalar çünkü kopyalayan yapıcı `new int[...]` ile yeni bir dizi
ayırır; kaynağa ham bir işaretçiyle bağlı, kendi kopyalayan yapıcısını doğru yazmamış bir
sınıf sığ kopyalar ve kopya da orijinaliyle aynı kaynağı paylaşır. Bir sınıfı okuyup "bu
kopyalanabilir mi, ve kopyalandığında ne olur" sorusuna cevap vermek için kopyalayan
yapıcının gövdesine bakmak gerekir — sınıfın adı ya da kullanım biçimi bunu söylemez.

#### Özet

- Bu dersin ölçtüğü ifade **kopya** sınıfındadır: `Tampon b(a)` `a`'dan yeni kurulmuş,
  adres olarak ayrı bir nesnedir.
- Kopyalayan yapıcı bir **kurma**da, kopyalayan atama bir **atama**da çağrılır; sol
  taraftaki nesnenin yeni mi var olan mı olduğu hangisinin çalışacağını belirler.
- `Tampon` kendi dizisini `new`'la ayırarak **derin kopya** üretir; üye üye kopyalama
  yalnız işaretçiyi kopylardı, bu **sığ kopya** olurdu.
- Kopyalayan atama `this == &digeri` denetimiyle **kendine atama**yı güvenle karşılar.
- Yıkıcı, kopyalayan yapıcı ve kopyalayan atamadan biri elle yazılırsa üçü de yazılmalıdır
  (üç kuralı).

#### Sonraki Adım

Bu ders bir nesnenin kopyalandığında kendi kaynağını aldığını ölçtü; bir sonraki ders
kopyalamadan uzaklaşıp bir **referansın** çağıranın nesnesini nasıl doğrudan değiştirdiğini
— hiçbir kopya üretmeden — ölçer.

### Referanslar

Source: https://academia.sh/tr/kurslar/cpp-temelleri/referanslar

Bir referans parametresinin çağıranın kendi nesnesiyle çalıştığını, hiçbir kopya üretmediğini ölçer.

Önceki ders bir nesnenin kopyalandığında kendi belleğini aldığını ölçtü. Bu ders tam tersi
bir soruyu ölçer: bir işlev bir nesneyi **referansla** alırsa, hiç kopya üretilir mi, yoksa
işlev doğrudan çağıranın kendi nesnesiyle mi çalışır? Ölçüm sınıfı **kendisi**: `tasi(Nokta&
n)` işlevine `p` verildiğinde, işlevin içindeki `n` ile çağırandaki `p` aynı nesnedir —
`&p == &n` doğrudur, aralarında hiçbir kopyalama adımı yoktur.

C'de bir işleve bir yapıyı "referansla" aktarmanın tek yolu bir işaretçi geçirmekti:
`tasi(&p)`, işlev içinde `(*ptr).x` ya da `ptr->x` ile erişim. Bu, çağıran tarafında
`&` operatörünün elle yazılmasını, işlev içinde de her erişimde bir yıldızlamanın (`*`)
ya da ok işlecinin gerekmesini zorunlu kılardı — ve `ptr`'nin `nullptr` olma ihtimali her
zaman vardı. C++'ta referans bu iki sorunu birden kaldırır: çağıran tarafında `tasi(p)`
yazılır, `&` gerekmez; işlev içinde `n.x` yazılır, yıldızlama gerekmez; ve bir referans
kurulmadan bırakılamayacağı için `nullptr` durumu dilin kendisi tarafından engellenir.

#### Referans Nedir

Bir **referans** (reference), var olan bir nesneye verilen ikinci bir addır — yeni bir
nesne değildir, yeni bir bellek adresi de almaz. `Nokta& r = p;` yazıldığında `r`, `p`'nin
kendisidir; `r` üzerinden yapılan her okuma ve yazma doğrudan `p`'nin belleğine ulaşır.
Bu, bir işaretçiden (`Nokta* ptr = &p;`) farklıdır: işaretçi ayrı bir nesnedir, kendi
belleğine sahiptir ve `p`'nin adresini **tutar**; referans ise `p`'nin kendisiyle **aynı**
şeydir, ayrı bir bellek konumu işgal etmez (derleyici çoğu zaman referansı bir işaretçi
gibi koda çevirir, ama dilin kendi modelinde referans yeni bir nesne değildir).

Bu ayrımın pratik sonucu şudur: bir işaretçi `ptr->x` yazmadan önce geçerli bir nesneye
işaret ettiğini garanti etmez, elle denetlenmesi gerekebilir (`if (ptr != nullptr)`). Bir
referans için böyle bir denetim yoktur ve gerekmez — dil, bir referansın her zaman geçerli
bir nesneye bağlı olduğunu varsayar (bu varsayımı kasıtlı olarak kıran, örneğin geçersiz
bir işaretçiyi yıldızlayıp bir referansa bağlayan kod, tanımsız davranış üretir, ama bu
dersin kapsamındaki hiçbir kod böyle bir şey yapmaz). Bu yüzden referans alan bir işlev
imzası, işaretçi alan bir imzadan daha az savunma kodu gerektirir — parametrenin var
olduğu baştan kabul edilir.

```cpp
// nokta.hpp — referansla aktarim olcumu icin basit bir yapi
#ifndef NOKTA_HPP
#define NOKTA_HPP

struct Nokta {
    int x;
    int y;
};

#endif
```

#### Referansla Aktarılan Yapı

```cpp
// referans-olcumu.cpp — tasi(Nokta&) cagiranin kendi nesnesiyle mi calisiyor
#include <cstdio>

#include "nokta.hpp"

void tasi(Nokta& n, Nokta* cagiranAdresi) {
    std::printf("tasi icindeki n cagiranla ayni nesne mi %s\n",
                &n == cagiranAdresi ? "evet" : "hayir");
    n.x += 10;
    n.y += 10;
}

int main() {
    Nokta p{1, 2};

    tasi(p, &p);

    std::printf("tasidan sonra p.x=%d p.y=%d\n", p.x, p.y);
    return 0;
}
```

```text
tasi icindeki n cagiranla ayni nesne mi evet
tasidan sonra p.x=11 p.y=12
```

**SK12.** `tasi(Nokta& n, Nokta* cagiranAdresi)` argümanını referansla alır;
`tasi(p, &p)` çağrıldığında `n`, `p`'nin **kendisidir** — `&n == cagiranAdresi` (yani
`&n == &p`) doğrudur. `n.x += 10;` satırı `n` üzerinden yazsa da, `n` ile `p` aynı nesne
olduğu için bu yazma doğrudan `p.x`'i değiştirir; `tasi` döndükten sonra `p.x` ve `p.y`
zaten güncellenmiştir (`11` ve `12`) — `main` hiçbir dönüş değeri almadan, hiçbir atama
yapmadan çağıranın nesnesi değişmiş olarak devam eder. Bu, bir önceki dersin **kopya**
sınıfının tam karşıtıdır: orada `b`, `a`'dan adres olarak farklıydı; burada `n`, `p` ile
**aynı** adrestir — hiçbir kopyalama adımı çalışmaz, çünkü referans argümanı
aktarmak kopyalayan yapıcıyı hiç çağırmaz.

#### Değer, Referans, const Referans: Ne Zaman Hangisi

Bir işlev bir nesneyi argüman olarak alırken üç seçenek vardır, ve her biri
farklı bir niyeti kodlar:

- **Değerle** (`Nokta n`): işlev kendi kopyasıyla çalışır, çağıranın nesnesini hiçbir
  biçimde etkilemez. Küçük, ucuz kopyalanan tipler için (bir `int`, iki `int`'lik bir
  `Nokta`) uygundur; büyük ya da pahalı kopyalanan tipler için (üçüncü dersteki `Tampon`
  gibi kendi belleğini yöneten bir sınıf) her çağrıda gereksiz bir kopya üretir.
- **Referansla** (`Nokta& n`): işlev çağıranın nesnesiyle doğrudan çalışır, onu
  **değiştirebilir**. `tasi` bunu yapar — imzası okuyana "bu işlev verdiğin nesneyi
  değiştirebilir" der.
- **const referansla** (`const Nokta& n`): işlev çağıranın nesnesine kopyasız erişir ama
  onu **değiştiremez** — `n.x += 10;` burada bir derleme hatası olurdu. Bu, "büyük bir
  nesneyi ucuza oku, ama değiştirme" niyetini kodlar; önceki derslerdeki kopyalayan
  yapıcı ve kopyalayan atamanın argümanı de tam bu yüzden `const Tampon&`dı.

İmza okunarak niyet anlaşılır: `void yaz(const Nokta& n)` çağıranın nesnesini yalnız okur;
`void tasi(Nokta& n)` değiştirir; `void kopyala(Nokta n)` hiç dokunmaz, kendi kopyasında
çalışır.

Bu üç seçenek arasında "her zaman referans kullan, daha hızlı" gibi bir kısayol yoktur.
`int` gibi küçük, tek bir makine kelimesine sığan bir tipi referansla almak genellikle
hiçbir şey kazandırmaz — bir referansı geçirmek de (derleyicinin ürettiği koda göre)
genellikle bir adres geçirmek anlamına gelir, ve bir adres de tipik olarak bir `int` ile
aynı büyüklüktedir; üstelik referans alan bir işlev artık çağıranın nesnesini
değiştirebileceği için, salt-okunur bir kullanım bile yanlışlıkla bir yazmaya açık hâle
gelir (bu risk `const int&` ile kapanır, ama o zaman da kazanç kalmaz). Değerle almak
küçük tipler için hem daha basit hem çoğunlukla eşit hızlıdır. Referans ve const
referansın asıl kazancı, kopyalaması pahalı olan tipler için ortaya çıkar — üçüncü ve
beşinci derslerdeki `Tampon` gibi kendi belleğini ayıran sınıflar, ya da çok sayıda üyesi
olan büyük yapılar.

#### Referans Parametreler ve Dönüş Değerleri

Referans yalnız parametrelerde değil, dönüş tiplerinde de kullanılabilir. `Nokta&
ilki(Nokta dizi[])` gibi bir işlev, dizinin ilk elemanını bir **kopya olarak değil,
kendisi olarak** döndürebilir — çağıran `ilki(d) = Nokta{0, 0};` yazarak dizinin ilk
elemanını doğrudan değiştirebilir, çünkü dönüş değeri `d[0]`'ın kendisidir. Bu ders
dönüş değeri referansı koşturmaz (bu, `operator[]` gibi bindirilmiş işleçlerin tipik
kullanım biçimidir ve K05'te işlenir), ama parametre referanslarıyla aynı ilkeye
dayanır: bir referans döndürmek, yeni bir nesne kurmadan var olan bir nesneye erişim
vermektir.

#### Referans Argümanlar Üye İşlevlerde

Bu ilke üye işlevler için de aynı biçimde işler. Beşinci dersteki `Tampon::operator=`
imzası `Tampon& operator=(const Tampon& digeri)` idi — `digeri` parametresi burada da
çağıranın kendi nesnesidir, `b = a;` yazıldığında `operator=`'in içindeki `digeri`, `a`
ile aynı adrestir. `operator=`'in `digeri.veri_`'yi okuduğu her satır, doğrudan `a`'nın
belleğini okur; eğer `digeri` değerle alınsaydı (`Tampon digeri` olarak), her `b = a;`
çağrısı önce bir `Tampon` kopyası kurar, `operator=` bu kopyayla çalışırdı — bu hem
gereksiz bir kopyalayan yapıcı çağrısı hem gereksiz bir yıkıcı çağrısı demektir. `const
Tampon&` seçimi burada da aynı niyeti taşır: `a`'yı kopyasız oku, değiştirme.

#### Referans Yeniden Bağlanamaz

Bir işaretçi kurulduktan sonra başka bir nesneyi göstermek üzere değiştirilebilir:
`ptr = &baska;`. Bir referans için bu mümkün değildir — `Nokta& r = p;` yazıldıktan sonra
`r = baska;` bir yeniden bağlama değildir, `baska`nın değerini `r`'nin (yani `p`'nin)
üzerine **atar**. Referans kurulduğu anda hangi nesneye bağlı olacağı sabitlenir ve bu bağ
referansın ömrü boyunca değişmez — bu yüzden bir referans **kurulurken** ilklendirilmek
zorundadır, boş bir referans (işaretçinin `nullptr`'ı gibi) yoktur.

#### const Referansın Geçiciye Bağlanması

```cpp
// gecici-baglama-olcumu.cpp — const referans geciciye baglanabiliyor mu
#include <cstdio>

#include "nokta.hpp"

void yaz(const Nokta& n) {
    std::printf("yaz icinde x=%d y=%d\n", n.x, n.y);
}

int main() {
    Nokta p{1, 2};
    yaz(p);

    const Nokta& r = Nokta{3, 4};
    std::printf("gecici uzerinden x=%d y=%d\n", r.x, r.y);

    yaz(Nokta{5, 6});
    return 0;
}
```

```text
yaz icinde x=1 y=2
gecici uzerinden x=3 y=4
yaz icinde x=5 y=6
```

**SK13.** `const Nokta& r = Nokta{3, 4};` satırı bir **geçici** nesneye (`Nokta{3, 4}`, adı
olmayan, ifadenin ortasında kurulan bir değer) `const` bir referans bağlar. Normalde bir
geçicinin ömrü onu kuran ifadenin sonunda biter, ama bir `const` referansa bağlandığında
C++ standardı bu geçicinin ömrünü referansın ömrüne kadar **uzatır** — `r` geçerli olduğu
sürece `Nokta{3, 4}` de geçerlidir, bu yüzden `r.x` ve `r.y` okumaları tanımsız davranış
değildir. Aynı bağlama `Nokta& r = Nokta{3, 4};` (const olmadan) yazılsaydı derlenmezdi:
sıradan bir referans bir geçiciye bağlanamaz, çünkü geçici üzerinde yazma yapılabilir
olması (`r.x = 99;` gibi) çağıranın hiç göremeyeceği, hemen sonra yok olacak bir nesneyi
değiştirmek anlamına gelirdi — dil bunu anlamsız bularak baştan engeller. `yaz(Nokta{5,
6})` çağrısı da aynı kuralı kullanır: `yaz`'ın parametresi `const Nokta&` olduğu için
geçici doğrudan bağlanabilir, parametre sıradan `Nokta&` olsaydı bu çağrı derlenmezdi.

#### Referans ve Bellek Düzeni

M11/K01'de bir yapının bellek düzeni — üye sırası, hizalama ve dolgu —
işlenmişti. Bir referans o dersteki anlamda "yeni bir bellek düzeni"
kurmaz — `Nokta& r = p;` yazıldığında `r`'nin kendi bir `sizeof` değeri ya da kendi bir
bellek düzeni yoktur, çünkü `r` yeni bir nesne değildir, `p`'nin başka bir adıdır. Bir
referans üyesi olan bir sınıf (`Nokta& referans_;` gibi bir üye) bu kuralın istisnası
gibi görünebilir — böyle bir üye genellikle bir işaretçi büyüklüğünde yer kaplar, çünkü
derleyici onu öyle koda çevirir — ama bu bir uygulama detayıdır, dilin kendisi referansın
"nerede" tutulduğunu tanımlamaz; yalnız hangi nesneye bağlı olduğunu tanımlar.

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

Bu dersin ölçtüğü ifade **kendisi** sınıfındadır — ama yalnız bazı durumlarda. `const
Nokta&` alan bir parametre hem **kendisi** hem **geçici** olabilir; imzanın kendisi
hangisi olduğunu söylemez, yalnız çağrı anındaki argüman belirler.

```cpp
// kendisi-mi-gecici-mi.cpp — const referans parametresi hangi durumda hangisi
#include <cstdio>

#include "nokta.hpp"

void yaz(const Nokta& n) {
    std::printf("yaz icinde x=%d y=%d\n", n.x, n.y);
}

int main() {
    Nokta p{7, 8};

    yaz(p);
    yaz(Nokta{9, 10});

    std::printf("p degismedi mi %s\n", (p.x == 7 && p.y == 8) ? "evet" : "hayir");
    return 0;
}
```

```text
yaz icinde x=7 y=8
yaz icinde x=9 y=10
p degismedi mi evet
```

**SK14.** `yaz(p)` çağrısında `n`, `p`'nin kendisidir — **kendisi** sınıfı; `yaz(Nokta{9,
10})` çağrısında ise `n`, ifadenin ortasında kurulan adsız bir geçiciye bağlıdır —
**geçici** sınıfı. İki çağrı da aynı imzayı (`const Nokta&`) kullanır, aynı işlevi
çağırır, ama parametrenin hangi sınıfa girdiği yalnız çağrı anındaki argümanın
kendisiyle belirlenir. `const` olması bu iki durumda da işlevin `n` üzerinden yazma
yapmasını engeller — bu yüzden `p` üçüncü satırda değişmeden kalır, `yaz` çağrısının `n`
kendisi mi geçici mi olduğunun `p`'nin durumu üzerinde hiçbir etkisi yoktur. Bir işlev
imzasına bakarak "bu parametre kendisi mi" sorusuna kesin cevap vermek mümkün değildir —
imza yalnız **değiştirilebilir mi** sorusuna (referans mı const referans mı) cevap verir;
kendisi mi geçici mi sorusunun cevabı her çağrıda yeniden belirlenir.

Bu sınır, sınıf tasarlarken düşünülmesi gereken bir sonuç doğurur: bir işlevin `const
Nokta&` parametresi kabul ettiğini görmek, o işlevin her zaman kalıcı bir nesneyle
çağrılacağını garanti etmez. İşlev gövdesi, aldığı referansın ömrünün çağrı ifadesinin
sonunda bitebileceğini varsaymalıdır — örneğin `n`'nin adresini bir yere saklayıp işlev
döndükten sonra kullanmak (`Nokta* p = &n; return p;` gibi), `n` bir geçiciye bağlıysa
sarkan bir işaretçi (dangling pointer) üretir. Bu ders böyle bir kodu koşturmaz; yalnız
sınırı belirtir. Bir sonraki dersteki `operator+`'ın neden bir referans değil **yeni bir
nesne** döndürdüğü de aynı sınırdan gelir: işlecin ürettiği toplam, işlecin kendi
gövdesinde kurulan yerel bir değerdir, işlev döndükten sonra da yaşaması gerekiyorsa
referansla değil, değerle döndürülmelidir.

#### Özet

- Bir referans var olan bir nesneye verilen ikinci bir addır; yeni bir nesne kurmaz,
  yeni bir adres almaz.
- Referansla alınan bir argüman çağıranın **kendisi**dir — hiçbir kopyalama
  adımı çalışmaz, `&n == &p` doğrudur.
- Değer, referans ve const referans arasındaki seçim niyeti kodlar: kopyala-ve-dokunma,
  değiştir, ya da kopyasız-oku-ama-değiştirme.
- Bir referans kurulduktan sonra başka bir nesneye yeniden bağlanamaz.
- `const` bir referans bir geçiciye bağlanabilir (geçicinin ömrü uzar); sıradan bir
  referans bağlanamaz.
- `const Nokta&` alan bir parametre çağrıya göre hem **kendisi** hem **geçici**
  olabilir; imza tek başına bunu söylemez.

#### Sonraki Adım

Bu ders bir referansın kopyasız, doğrudan çağıranın nesnesiyle çalıştığını ölçtü; bir
sonraki ve son ders bir işlecin (`operator+`) neden yeni bir **geçici** döndürdüğünü, ve
`operator+=`'in neden bunun yerine `*this`'i referansla döndürdüğünü ölçer.

### İşleç Aşırı Yükleme

Source: https://academia.sh/tr/kurslar/cpp-temelleri/islec-asiri-yukleme

operator+'nın neden yeni bir geçici nesne döndürdüğünü, operator+='in neden bunun yerine kendi nesnesini referansla döndürdüğünü ölçer.

Bu, K04'ün bu bölümdeki son dersidir. Önceki ders bir referansın hiçbir kopya üretmeden
çağıranın kendi nesnesiyle çalıştığını ölçtü. Bu ders, bir **işlecin** (operator) bazen
tam tersini yaptığını — yeni bir nesne ürettiğini — ölçer. Ölçüm sınıfı **geçici**:
`a + b` ifadesinin sonucu, ne `a` ne `b`'dir; işlecin gövdesinde kurulan, ifadenin sonunda
yok olan yeni bir `Para` nesnesidir.

C'de böyle bir toplamanın tek yolu adı açık bir işlevdi: `Para toplama_yap(Para a, Para
b)` gibi bir işlev çağrılır, sonucu ayrı bir değişkene atanırdı — `a + b` yazmak mümkün
değildi, çünkü `+` yalnız yerleşik sayısal tipler için tanımlıydı. C++'ta `operator+`
aşırı yüklemesi, `Para` gibi kullanıcı tanımlı bir tip için de `+` sözdizimini kullanıma
açar; çağıran taraf `toplama_yap(a, b)` yerine `a + b` yazar. Bu, sözdiziminin okunurluğunu
artırır, ama gövdenin ne yaptığını (yeni bir nesne mi kurduğunu, var olan bir nesneyi mi
değiştirdiğini) gizler de — bu yüzden bu dersin ölçümü tam olarak bunu, işlecin
sözdiziminin arkasında hangi nesnenin üretildiğini, açığa çıkarır.

#### İşleç Aşırı Yükleme Nedir

**İşleç aşırı yükleme** (operator overloading), `+`, `==`, `<<` gibi yerleşik işleçlerin
kullanıcı tanımlı bir sınıf için ne anlama geldiğini tanımlamaktır. `operator+` bir üye
işlev olarak yazıldığında, `a + b` ifadesi derleyici tarafından `a.operator+(b)` olarak
okunur — sözdizimi `+` kalır, ama çağrılan şey sınıfın kendi yazdığı bir işlevdir.
Aşırı yükleme yeni bir sözdizimi eklemez; var olan işleç sözdizimini kullanıcı tanımlı
tipler için **anlamlandırır**.

Aşırı yükleme bir işlecin **kaç argüman** aldığını ya da **öncelik sırasını**
değiştiremez — `+` her zaman ikili kalır, `*`'ten sonra değerlendirilmeye devam eder.
Değişebilen tek şey işlecin argümanları için ne yaptığıdır. Bu sınır kasıtlıdır:
`operator+`'yı çağıran biri, `Para` için de tıpkı `int` için olduğu gibi "iki değeri alıp
bir sonuç üretir" davranışını bekler; aşırı yükleme bu beklentiyi bozacak bir sözdizimi
değişikliği yapamaz, yalnız "sonuç" ve "değiştirme" kısmının ne anlama geldiğini
tanımlayabilir.

```cpp
// para.hpp — kurus cinsinden tutar tutan, islec asiri yuklemesi icin bir sinif
#ifndef PARA_HPP
#define PARA_HPP

#include <cstdio>

class Para {
public:
    explicit Para(long kurus) : kurus_(kurus) {}

    Para operator+(const Para& digeri) const {
        return Para(kurus_ + digeri.kurus_);
    }

    Para& operator+=(const Para& digeri) {
        kurus_ += digeri.kurus_;
        return *this;
    }

    bool operator==(const Para& digeri) const {
        return kurus_ == digeri.kurus_;
    }

    long kurus() const { return kurus_; }

private:
    long kurus_;
};

inline void yaz_para(const Para& p) {
    std::printf("kurus=%ld\n", p.kurus());
}

#endif
```

#### `operator+`: Yeni Bir Geçici Döndürür

```cpp
// artı-olcumu.cpp — a + b sonucu yeni bir gecici mi
#include <cstdio>

#include "para.hpp"

int main() {
    Para a(100);
    Para b(250);

    Para c = a + b;

    std::printf("c ile a ayni nesne mi %s\n", &c == &a ? "evet" : "hayir");
    std::printf("c ile b ayni nesne mi %s\n", &c == &b ? "evet" : "hayir");
    yaz_para(a);
    yaz_para(b);
    yaz_para(c);
    return 0;
}
```

```text
c ile a ayni nesne mi hayir
c ile b ayni nesne mi hayir
kurus=100
kurus=250
kurus=350
```

**SK15.** `a + b` ifadesi `a.operator+(b)` çağrısına dönüşür; `operator+`'nın gövdesi
`return Para(kurus_ + digeri.kurus_);` ile **yeni** bir `Para` kurar ve bunu **değerle**
döndürür — `a`'yı da `b`'yi de değiştirmez, ikisi de kendi `kurus_` değerini korur (`100`
ve `250`). Döndürülen nesne `c`'ye kurulur; `c`'nin adresi ne `a`'nınkiyle ne `b`'ninkiyle
aynıdır. `operator+`'nın `const` işaretli olması (`operator+(const Para& digeri) const`)
hem `digeri`'nin hem çağrıldığı nesnenin (`this`) değiştirilmediğini garanti eder — bir
toplama işleci, tanım gereği, toplananları değil yalnız sonucu üretmelidir.

#### `operator+=`: Kendi Nesnesini Değiştirip Referansla Döner

```cpp
// arti-esittir-olcumu.cpp — a += b, a'nin kendisini mi degistiriyor
#include <cstdio>

#include "para.hpp"

int main() {
    Para a(100);
    Para b(250);

    Para& sonuc = (a += b);

    std::printf("sonuc ile a ayni nesne mi %s\n", &sonuc == &a ? "evet" : "hayir");
    yaz_para(a);
    return 0;
}
```

```text
sonuc ile a ayni nesne mi evet
kurus=350
```

**SK16.** `operator+=`'in gövdesi `kurus_ += digeri.kurus_;` ile **kendi** `kurus_`
üyesini değiştirir, sonra `return *this;` ile **kendisini** referansla döndürür — yeni bir
nesne kurmaz. `a += b` çağrıldığında `a`'nın kendisi değişir (`350` olur) ve
`(a += b)`'nin sonucu da yine `a`'nın kendisidir: `&sonuc == &a` doğrudur. Bu, `operator+`
ile taban tabana zıttır: `operator+` iki toplananı da değiştirmeden yeni bir **geçici**
üretirken, `operator+=` tek bir nesneyi (soldaki) değiştirip **kendisi** sınıfında bir
referans döner. İki işlecin dönüş tipi bu farkı imzada taşır — `operator+` değerle
(`Para`), `operator+=` referansla (`Para&`) döner; imza okunarak hangisinin yeni bir
nesne ürettiği, hangisinin var olan bir nesneyi değiştirdiği önceden bilinebilir.

#### Anlamlı İşleç Tanımlamanın Sınırları

Bir işlecin kullanıcı tanımlı bir sınıf için aşırı yüklenebiliyor olması, her sınıfın her
işleci tanımlaması gerektiği anlamına gelmez. Birkaç yerleşik kural işleç tanımlarını
tutarlı tutar:

- **`+`, `-`, `*` gibi ikili aritmetik işleçler** genellikle üye değil **serbest işlev**
  (free function) olarak yazılır (`Para operator+(const Para& a, const Para& b)`), çünkü
  bu, sol tarafı `Para` olmayan ifadelerde de (örneğin gelecekte bir `int` ile `Para`
  toplanacaksa) simetrik dönüşüm sağlar. `Para` bu dersin ölçümü için `operator+`'yı üye
  olarak tanımlar; serbest işlev biçimi yalnız burada anılır.
- **`+`, `+=` üzerinden yazılabilir**: `operator+`'nın gövdesi `kurus_ + digeri.kurus_`
  yerine `Para sonuc(kurus_); sonuc += digeri; return sonuc;` biçiminde de yazılabilirdi
  — `+=`'in mantığını tekrar etmeden `+`'yı ona dayandırmak, iki işlecin davranışının
  birbirinden sapmasını engeller. `Para` bu dersin ölçümünü sade tutmak için ikisini
  bağımsız yazar.
- **`==` bir eşitlik**, bir sıralama değildir; `Para::operator==` iki nesnenin `kurus_`
  değerlerini karşılaştırır, `<` gibi bir sıralama işleci bu derste tanımlanmaz.
- **`<<` (akış çıktısı) bir üye işlev olamaz** — `std::cout << p` ifadesinde sol taraf
  `std::ostream`dur, `Para` değil; bu yüzden `operator<<` bir serbest işlev olarak
  yazılır ve `Para`'nın `private` üyesine erişmesi gerekiyorsa `friend` bildirimiyle
  (ikinci derste yalnız adıyla anılmıştı) izin verilir. Bu ders `operator<<` tanımlamaz,
  `yaz_para` adında sıradan bir işlevle aynı işi görür.

Bir işleci aşırı yüklerken sorulması gereken soru şudur: bu işlecin yerleşik anlamı
(`+` toplar, `==` karşılaştırır, `<<` yazdırır) sınıf için hâlâ **doğal** mı? `Para` için
`+` toplamı, `==` eşitliği anlamlı biçimde karşılar; ama örneğin `Para operator*(const
Para& a, const Para& b)` iki tutarı çarpmanın parasal bir anlamı olmadığı için tanımsız
bırakılabilirdi — bu ders böyle bir işleç yazmaz.

Bu sınırın ihlali derlenir ama okunmaz kılar. C++ hiçbir işlecin hangi anlamda
kullanılacağını zorlamaz — `operator+`'nın gövdesine teknik olarak bir çıkarma yazılabilir,
derleyici buna itiraz etmez. Ama böyle bir kod, `a + b` yazan herkesin beklediği
("toplanıyor") ile gerçekte olanı ("çıkarılıyor") ayırır — bu, sınıfın kullanıcısını
yanıltan, hatası derleme zamanında yakalanamayan bir tasarım kusurudur. Aşırı yükleme
gücünü kısıtlayan tek şey derleyici değil, işlecin okuyucuda uyandırdığı beklentiye sadık
kalma disiplinidir.

#### `explicit`, Kopyalayan Yapıcı ve İşleçlerin Buluştuğu Yer

`Para`'nın yapıcısı `explicit Para(long kurus)` olarak tanımlıdır — üçüncü dersteki
`explicit` kuralı burada da geçerlidir: `Para p = 100;` gibi örtük bir dönüşüm derlenmez,
`Para p(100);` ya da `Para p{100};` yazılmalıdır. Bu, işleç aşırı yüklemesiyle doğrudan
etkileşir: `operator+`'nın gövdesindeki `return Para(kurus_ + digeri.kurus_);` satırı
**açık** bir kurma yazar; eğer yapıcı `explicit` olmasaydı ve gövde yalnız `return kurus_ +
digeri.kurus_;` yazsaydı, `long` sonucu örtük olarak bir `Para`'ya dönüştürülebilirdi —
bu, üçüncü dersin uyardığı türden, istenmeyen bir örtük dönüşüm zincirine kapı açardı
(örneğin bir `Para`'nın yanlışlıkla bir `long`la, dolaylı yoldan, karşılaştırılabilmesi
gibi). `operator+`'nın sonucu her zaman açıkça bir `Para` kurarak döndürülür; bu hem
`explicit` kuralına uyar hem işlecin ne ürettiğini kod içinde açık tutar. Beşinci dersteki
kopyalayan yapıcı da burada örtük olarak devreye girer: `return Para(...)` satırı yeni bir
`Para` değeri döndürdüğünde, derleyici çoğu durumda bu değeri doğrudan çağıranın alacağı
yere kurar (değer kopyalamayı atlayan bir optimizasyon), ama kavramsal model hâlâ "yeni bir
nesne kuruldu ve döndürüldü"dür — kopyalayan yapıcının hiç çağrılmaması bir optimizasyon
ayrıntısıdır, `operator+`'nın yeni bir nesne ürettiği gerçeğini değiştirmez.

#### Değer Semantiği: Bu Kursun Bağlamı

M11/K01'de bir yapının atamayla bütün alanlarıyla kopyalandığı görülmüştü; bu, değer
semantiğidir (value semantics): bir değer atandığında ya da aktarıldığında, iki taraf birbirinden bağımsız hâle gelir. `Para`
bu ilkeyi C++'ın kendi araçlarıyla — kopyalayan yapıcı, kopyalayan atama, ve şimdi
`operator+`'nın yeni bir değer döndürmesiyle — uygular. `operator+=` ise kasıtlı olarak
bu ilkenin dışına çıkar: kendi nesnesini değiştirir, çünkü `+=`'in yerleşik anlamı zaten
"soldaki değeri güncelle"dir (`x += 1` de `x`'i değiştirir, yeni bir değişken kurmaz).
Bir sınıf için işleç yazarken bu iki davranış biçiminden hangisinin seçildiği, işlecin
yerleşik `int`/`long` karşılığının davranışıyla tutarlı olmalıdır — `Para` bu tutarlılığı
korur: `+` yeni bir değer üretir, `+=` var olanı değiştirir, tıpkı `int` için olduğu gibi.

#### Kopya, Geçici ve Kendisi Ayrımının Özeti

Bu konunun yedi dersi tek bir soruyu farklı açılardan ölçtü: bir ifade **hangi nesneyle**
çalışıyor? Üçüncü ve dördüncü dersler bir nesnenin kendi kaynağını nasıl aldığını (yapıcı,
yıkıcı, RAII); beşinci ders bir kopyanın orijinalinden nasıl ayrıştığını (**kopya**);
altıncı ders bir referansın hiç kopyalamadan çağıranın **kendisi**yle nasıl çalıştığını;
bu ders ise bir işlecin ne zaman yeni bir **geçici** ürettiğini, ne zaman kendi nesnesini
değiştirip **kendisi**ni döndürdüğünü ölçtü. Dört sınıf — **kendisi**, **kopya**,
**parça**, **geçici** — bir C++ ifadesini okurken sorulacak tek soruya karşılık gelir:
bu ifadenin ürettiği ya da işlediği nesne, başka hangi nesneyle aynı, ve hangisinden
farklı?

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

```cpp
// zincirleme-olcumu.cpp — a + b + c kac gecici uretir
#include <cstdio>

#include "para.hpp"

int main() {
    Para a(10);
    Para b(20);
    Para c(30);

    Para toplam = a + b + c;

    yaz_para(toplam);
    return 0;
}
```

```text
kurus=60
```

**SK17.** `a + b + c` ifadesi soldan sağa değerlendirilir: önce `a + b` çalışır ve
`kurus_=30` değerinde adsız bir `Para` üretir; sonra bu sonuç `+ c` çağrısının sol tarafı
olur. `operator+` bir üye işlev olduğu için ikinci çağrının `this`'i bir nesneyi göstermek
zorundadır: `a + b`'nin sonucu bu yüzden **bir geçici** olarak kurulur ve tam ifade
(`a + b + c;`nin sonu, noktalı virgül) bitene kadar yaşar, sonra yıkılır. İkinci
`operator+` çağrısının `kurus_=60` değerindeki sonucu ise ayrı bir geçici olarak
kurulmaz: C++17'nin garantili kopya seçimi (guaranteed copy elision) gereği doğrudan
`toplam`ın kendi deposunda kurulur. İki `operator+` çağrısı, dolayısıyla **bir** geçici;
`toplam` ikinci çağrının sonucunun ta kendisidir, bir kopyası değil. `Para`'nın yıkıcısı
burada bir `printf` içermediği için geçicinin kurulma ve yıkılma anı bu blokta
gözlemlenmiyor; sayı dilin kuralından okunur, bu ölçümden değil. Zincirleme bir
toplamanın "kaç geçici ürettiği" sorusunun cevabı, işlecin kaç kez çağrıldığından çok,
her sonucun nereye gittiğine bağlıdır: bir sonraki çağrının nesnesi olan ara sonuç
geçicidir, bir değişkeni ilklendiren son sonuç o değişkenin kendisidir.

#### Özet

- Bu dersin ölçtüğü ifade **geçici** sınıfındadır: `operator+` toplananları değiştirmeden
  yeni ve adsız bir `Para` döndürür; dönüş tipi değerledir (`Para`, referans değil).
- `operator+=` kendi nesnesini değiştirir ve **kendisini** (`*this`) referansla
  döndürür; dönüş tipi `Para&`dır.
- İkili aritmetik işleçler genellikle serbest işlev olarak yazılır; `<<` bir üye işlev
  olamaz çünkü sol tarafı sınıfın kendisi değildir.
- Bir işleç yalnız yerleşik anlamı sınıf için doğal olduğunda tanımlanmalıdır.
- Zincirleme bir ifadede (`a + b + c`) yalnız ara sonuç (`a + b`) geçici olarak kurulur
  ve tam ifade bitene kadar yaşar; son sonuç doğrudan `toplam`ın içinde kurulur.
- Bu konunun yedi dersi aynı soruyu sordu: bir C++ ifadesi **kendisi**, **kopya**,
  **parça** ya da **geçici** nesnelerden hangisiyle çalışıyor?

#### Sonraki Adım

Bu konu, bir nesnenin kendi kaynağını nasıl aldığını, kopyalandığını, referansla
aktarıldığını ve işleçler aracılığıyla yeni nesneler ürettiğini ölçtü. Bir sonraki konu
bu tek nesne modelinden uzaklaşıp sınıflar arasındaki **ilişkiye** — bir sınıfın başka
bir sınıfın davranışını nasıl genişlettiğine ve çalışma zamanında hangi işlevin
çağrılacağının nasıl belirlendiğine — geçer.

## Çok Biçimlilik ve Yapı

Kalıtım ve taban alt nesnesi, sanal fonksiyonlar ve sanal tablolar, soyut sınıflar, çoklu kalıtım ve elmas sorunu, istisnalar, ad alanları ve tip dönüşüm işleçleri — her biri bir ifadenin hangi nesneyle çalıştığı sorusuyla birlikte.

### Kalıtım

Source: https://academia.sh/tr/kurslar/cpp-temelleri/kalitim

Bir türetilmiş nesnenin içinde taşıdığı taban alt nesnesinin, aynı adreste durduğunun ve tek bir kurma/yıkma sırasının parçası olduğunun ölçümü.

Önceki konu bir nesnenin kendi kaynağını nasıl kurduğunu, kopyaladığını ve serbest
bıraktığını ölçtü; sınıf tek başınaydı, başka bir sınıfa bağlı değildi. Bu konu bir
sınıfın başka bir sınıfın üzerine nasıl kurulduğunu ele alıyor: **kalıtım**, bir sınıfı
bir başkasının içine yerleştirir. Dersin ölçü sorusu aynı — bu ifade hangi nesneyle
çalışıyor — ama artık nesnenin kendisi iki katmanlı: bir **taban alt nesne** ve onu saran
bir türetilmiş nesne. Bu ders şunu soruyor: taban alt nesne, türetilmiş nesneden ayrı bir
nesne mi, yoksa onun bir parçası mı?

#### Taban Alt Nesnesi ve Ortak Adres

`Yonetici`, `Calisan`'dan türüyor. `Yonetici` kurulduğunda önce `Calisan` yapıcısı
çalışıyor, sonra `Yonetici`'nin kendi gövdesi. Taban tipinden bir işaretçiyle (`taban`)
`y`'yi adlandırmak bir kopya çıkarmıyor mu, yoksa aynı adresi mi taşıyor — bunu adres
karşılaştırmasıyla ölçmek gerekiyor.

```cpp
// kalitim_taban.cpp — turetilmis nesnenin taban alt nesnesi ve ortak adres
#include <iostream>
#include <string>

class Calisan {
public:
    explicit Calisan(std::string ad) : ad_(ad) {
        std::cout << "Calisan yapici: " << ad_ << "\n";
    }
    ~Calisan() {
        std::cout << "Calisan yikici: " << ad_ << "\n";
    }
protected:
    std::string ad_;
};

class Yonetici : public Calisan {
public:
    Yonetici(std::string ad, int takim_no) : Calisan(ad), takim_no_(takim_no) {
        std::cout << "Yonetici yapici: takim " << takim_no_ << "\n";
    }
    ~Yonetici() {
        std::cout << "Yonetici yikici\n";
    }
private:
    int takim_no_;
};

int main() {
    Yonetici y("Aylin", 4);
    Calisan* taban = &y;

    std::cout << std::boolalpha;
    std::cout << "y ile taban adresinin ayni olup olmadigi "
              << (static_cast<void*>(&y) == static_cast<void*>(taban)) << "\n";

    return 0;
}
```

```
Calisan yapici: Aylin
Yonetici yapici: takim 4
y ile taban adresinin ayni olup olmadigi true
Yonetici yikici
Calisan yikici: Aylin
```

**CB1.** `Yonetici` yapıcısı önce `Calisan` yapıcısını çağırıyor ("Calisan yapici:
Aylin"), sonra kendi gövdesini çalıştırıyor ("Yonetici yapici: takim 4") — bu, iki ayrı
nesnenin art arda kurulması değil, **tek bir nesnenin** iki aşamalı kurulması. `taban =
&y` ataması bir kopya çıkarmıyor, yalnız aynı adresi taban tipinden bir işaretçiyle
adlandırıyor: `static_cast<void*>(&y) == static_cast<void*>(taban)` karşılaştırması
bunu doğruluyor, **true**. Çıkışta `y` kapsam dışına çıkarken yıkım sırası kurmanın tam
tersini izliyor — önce `Yonetici`'nin kendi yıkıcısı, sonra `Calisan`'ınki — yine tek
nesnenin iki aşamalı çözülüşü. (M08/K02'de sorulan soru çağrıyı hangi tipin çözdüğüydü;
burada soru değişti — çağrı değil, ifadenin işaret ettiği **nesne** ölçülüyor, ve
Java'da nesne dilimleme yokken C++'ta taban tipe **değerle** atama yeni ve eksik bir
nesne kurar; burada yalnız işaretçi erişimi var, dilimleme yok.) Sınıf **parça**:
`taban`'ın gösterdiği şey `y`'den bağımsız bir nesne değil, `y`'nin içindeki taban alt
nesnesi.

#### Taban Referansıyla Erişim: Parçaya Erişim, Ayrı Nesne Değil

Bir işleve `Calisan&` parametresiyle bir `Yonetici` geçirmek de aynı soruyu soruyor:
referans parametre `y`'den ayrı bir nesneye mi bağlanıyor, yoksa `y`'nin içindeki taban
parçasına mı?

```cpp
// kalitim_referans.cpp — taban referansiyla erisim ayni parcaya ulasiyor
#include <iostream>
#include <string>

class Calisan {
public:
    explicit Calisan(std::string ad) : ad_(ad) {}
    std::string ad() const { return ad_; }
protected:
    std::string ad_;
};

class Yonetici : public Calisan {
public:
    Yonetici(std::string ad, int takim_no) : Calisan(ad), takim_no_(takim_no) {}
    int takim_no() const { return takim_no_; }
private:
    int takim_no_;
};

bool ayni_nesne_mi(const Calisan& c, const Yonetici& y) {
    return static_cast<const void*>(&c) == static_cast<const void*>(&y);
}

void etiketle(const Calisan& c) {
    std::cout << "referansin gordugu ad " << c.ad() << "\n";
}

int main() {
    Yonetici y("Baris", 7);

    etiketle(y);

    std::cout << std::boolalpha;
    std::cout << "referans parametrenin y ile ayni nesneyi gosterip gostermedigi "
              << ayni_nesne_mi(y, y) << "\n";

    return 0;
}
```

```
referansin gordugu ad Baris
referans parametrenin y ile ayni nesneyi gosterip gostermedigi true
```

**CB2.** `etiketle(y)` çağrısında `y` bir `Calisan&`'a bağlanıyor; `ad()` çağrısı
`y`'nin içindeki `Calisan` parçasının `ad_` alanını okuyor ve "Baris" basıyor —
`Yonetici`'nin eklediği `takim_no_`'ya hiç erişmeden. `ayni_nesne_mi(y, y)` çağrısında
ilk parametre `y`'yi `Calisan&`'a, ikinci parametre aynı `y`'yi `Yonetici&`'ya
bağlıyor; iki referansın adresi karşılaştırıldığında sonuç **true**. Referans bağlama
burada da bir kopya kurmuyor — parametre, çağrıda adı geçen nesnenin kendisine (daha
doğrusu, o nesnenin taban parçasına) bağlanıyor. Sınıf yine **parça**: `Calisan&`
parametresinin gösterdiği şey, `y`'nin dışında yaşayan bir nesne değil, `y`'nin
içindeki taban alt nesnesi.

#### Taşan Bir Nesnenin Boyutu: Parça Taban Nesneyi Büyütür

Adres eşitliği taban alt nesnenin `y`'nin başında durduğunu gösteriyor; boyut
karşılaştırması ise taban alt nesnenin `y`'nin **içinde**, ayrı bir kutuda değil, aynı
bellek bölgesinde yer kapladığını gösteriyor.

```cpp
// kalitim_boyut.cpp — turetilmis nesnenin boyutu taban parcayi icine aliyor
#include <iostream>
#include <string>

class Calisan {
public:
    explicit Calisan(std::string ad) : ad_(ad) {}
protected:
    std::string ad_;
};

class Yonetici : public Calisan {
public:
    Yonetici(std::string ad, int takim_no) : Calisan(ad), takim_no_(takim_no) {}
private:
    int takim_no_;
};

int main() {
    std::cout << std::boolalpha;
    std::cout << "yonetici boyutunun calisan arti en az bir int kadar olup olmadigi "
              << (sizeof(Yonetici) >= sizeof(Calisan) + sizeof(int)) << "\n";
    std::cout << "yonetici boyutunun calisandan buyuk olup olmadigi "
              << (sizeof(Yonetici) > sizeof(Calisan)) << "\n";
    return 0;
}
```

```
yonetici boyutunun calisan arti en az bir int kadar olup olmadigi true
yonetici boyutunun calisandan buyuk olup olmadigi true
```

**CB3.** `sizeof(Yonetici)`, `Calisan` alt nesnesinin tam boyutunu ve `Yonetici`'nin
kendi eklediği `takim_no_` alanını birlikte taşıyor — iki karşılaştırma da **true**.
Bu, ilk iki ölçümün adres eşitliğini bellek düzeni tarafından doğruluyor: taban alt
nesne `y`'nin içinde ayrı, kendi başına ayrılmış bir kutuda durmuyor; `y`'nin fiziksel
bir bölümü olarak, `Yonetici`'nin kendi alanlarıyla aynı bellek bloğunun içinde yer
kaplıyor. Sınıf **parça**: `Yonetici` nesnesinin boyutu, taşıdığı `Calisan` parçasının
boyutunu içeriyor, ona ek bir referans ya da işaretçi olarak değil.

#### Genel, Korumalı ve Özel Kalıtım: Adres Aynı, Dışarı Açıklık Farklı

Üç sınıf aynı `Taban`'dan farklı erişim belirleyicileriyle türüyor: `Genel` `public`,
`Korumali` `protected`, `Ozel` `private` kalıtımla. Üçü de kendi içinde `Taban`'ın
korumalı üyesine erişebiliyor mu? Ve dışarıdan `Taban*`'a yalnız hangisi dönüştürülebiliyor?

```cpp
// kalitim_erisim_belirleyicileri.cpp — genel korumali ozel kalitim ayni adresi tasiyor, farkli disari aciklik saglar
#include <iostream>

class Taban {
protected:
    int deger_ = 42;
};

class Genel : public Taban {
public:
    int oku() const { return deger_; }
};

class Korumali : protected Taban {
public:
    int oku() const { return deger_; }
};

class Ozel : private Taban {
public:
    int oku() const { return deger_; }
};

int main() {
    Genel g;
    Korumali k;
    Ozel o;

    std::cout << "genel kalitimda oku() " << g.oku() << "\n";
    std::cout << "korumali kalitimda oku() " << k.oku() << "\n";
    std::cout << "ozel kalitimda oku() " << o.oku() << "\n";

    Taban* taban_g = &g;
    std::cout << std::boolalpha
              << "genel kalitimda disaridan alinan taban* adresinin g ile ayni olup olmadigi "
              << (static_cast<void*>(taban_g) == static_cast<void*>(&g)) << "\n";

    return 0;
}
```

```
genel kalitimda oku() 42
korumali kalitimda oku() 42
ozel kalitimda oku() 42
genel kalitimda disaridan alinan taban* adresinin g ile ayni olup olmadigi true
```

**CB19.** Üç sınıfın da `oku()` işlevi `deger_`'e sınıfın kendi gövdesi içinden erişiyor
ve üçü de **42** basıyor — erişim belirleyicisi (`public`, `protected`, `private`)
sınıfın kendi içindeki erişimi değiştirmiyor, çünkü `protected` bir üye, türeyen
sınıfın kendi gövdesinden zaten erişilebilir. Değişen şey **dışarıdan** görünürlük:
`Taban* taban_g = &g;` yalnız `Genel` `public` kalıtımla türediği için doğrudan
derleniyor — `Korumali` ya da `Ozel`'den aynı dönüştürme yapılsaydı, taban alt nesne
dışarıya `Taban*` olarak sunulamazdı. Adres karşılaştırması yine **true**: erişim
belirleyicisi, `Genel`'in bellek düzenini ya da taban alt nesnenin adresini
değiştirmiyor, yalnız o adrese dışarıdan hangi işaretçi tipiyle ulaşılabileceğini
sınırlıyor. Sınıf **parça**: `oku()`'nun çalıştığı `deger_`, hangi erişim
belirleyicisiyle türetilmiş olursa olsun, hep sınıfın kendi taban parçası — erişim
belirleyicisi bunu görünür kılıp kılmamayı seçiyor, nesneyi değiştirmiyor.

#### Üç Katmanlı Yapıcı Zinciri

İlk ölçüm iki katmanlı bir zinciri (`Calisan` → `Yonetici`) gösterdi. Zincir üçüncü bir
katmana (`A` → `B` → `C`) uzatıldığında kurma ve yıkma sırası nasıl genişliyor?

```cpp
// kalitim_uc_katmanli_zincir.cpp — uc katmanli yapici zinciri en tabandan en turemise
#include <iostream>

class A {
public:
    A() { std::cout << "A yapici\n"; }
    ~A() { std::cout << "A yikici\n"; }
};

class B : public A {
public:
    B() { std::cout << "B yapici\n"; }
    ~B() { std::cout << "B yikici\n"; }
};

class C : public B {
public:
    C() { std::cout << "C yapici\n"; }
    ~C() { std::cout << "C yikici\n"; }
};

int main() {
    std::cout << "-- kurma --\n";
    {
        C c;
        std::cout << "-- kapsam sonu, yikim basliyor --\n";
    }
    return 0;
}
```

```
-- kurma --
A yapici
B yapici
C yapici
-- kapsam sonu, yikim basliyor --
C yikici
B yikici
A yikici
```

**CB20.** `C c;` tek bir nesne kuruyor, ama bu kurma üç aşamaya ayrılıyor: önce en
tabandaki `A`, sonra `B`, sonra `C`'nin kendi gövdesi — zincir kaç katmanlı olursa
olsun sıra hep **en tabandan en türemişe**. Yıkım, kapsam sonunda tam tersi sırayla
işliyor: önce `C`, sonra `B`, sonra `A`. Bu, ilk ölçümdeki iki aşamalı kurma/yıkmanın
genellemesi — `C` nesnesi aslında üç iç içe parçadan oluşuyor (`A` alt nesnesi, onun
içinde büyüyen `B` alt nesnesi, onun içinde büyüyen `C`'nin kendi alanları), ve her
parça yalnız kendinden önceki parça tamamlandıktan sonra kuruluyor. Sınıf **parça**:
`C` tek bir nesne, ama o nesnenin üç katmanı var ve her katman bir öncekinin üzerine
inşa ediliyor.

#### Tabana Değerle Atama Nesneyi Diliyor: Ayrı Bir Kopya

Önceki ölçümler hep işaretçi ya da referans üzerinden taban parçasına eriştiler — adres
hep aynıydı. `Yonetici` nesnesi `Calisan` tipinde **değerle** bir değişkene atandığında
adres de aynı mı kalıyor, yoksa bağımsız bir nesne mi ortaya çıkıyor?

```cpp
// kalitim_dilimleme.cpp — tabana degerle atama nesneyi diliyor, ayri ve bagimsiz bir kopya olusuyor
#include <iostream>
#include <string>

class Calisan {
public:
    explicit Calisan(std::string ad) : ad_(std::move(ad)) {}
    std::string ad() const { return ad_; }
    void ad_ata(std::string ad) { ad_ = std::move(ad); }
protected:
    std::string ad_;
};

class Yonetici : public Calisan {
public:
    Yonetici(std::string ad, int takim_no) : Calisan(std::move(ad)), takim_no_(takim_no) {}
    int takim_no() const { return takim_no_; }
private:
    int takim_no_;
};

int main() {
    Yonetici y("Deniz", 9);
    Calisan dilimlenmis = y;

    std::cout << std::boolalpha;
    std::cout << "dilimlenmis ile y'nin ayni adresi tasiyip tasimadigi "
              << (static_cast<void*>(&dilimlenmis) == static_cast<const void*>(&y)) << "\n";

    dilimlenmis.ad_ata("Baska");
    std::cout << "dilimlenmis adi degistirildikten sonra y'nin adi " << y.ad() << "\n";
    std::cout << "dilimlenmis nesnenin adi " << dilimlenmis.ad() << "\n";

    return 0;
}
```

```
dilimlenmis ile y'nin ayni adresi tasiyip tasimadigi false
dilimlenmis adi degistirildikten sonra y'nin adi Deniz
dilimlenmis nesnenin adi Baska
```

**CB21.** `Calisan dilimlenmis = y;` bir işaretçi ya da referans değil, **yeni bir
`Calisan` nesnesi** kuruyor — `Calisan`'ın kopya kurucusu `y`'nin taban parçasından
`ad_`'i kopyalıyor, `Yonetici`'nin eklediği `takim_no_` ise hiç taşınmıyor, çünkü
`Calisan`'ın kendi tanımında öyle bir alan yok. Adres karşılaştırması bunu doğruluyor:
**false** — `dilimlenmis`, `y`'den bağımsız, kendi adresinde yaşayan ayrı bir nesne.
`dilimlenmis`'in adı değiştirildiğinde `y`'nin adı **Deniz** olarak kalıyor: iki nesne
birbirinden bağımsız. Bu, dersin geri kalanından **farklı bir sınıf**: işaretçi ve
referans erişimi hep aynı taban **parçasına** ulaşırken, değerle atama (dilimleme)
`Calisan` tipinde yeni, bağımsız bir **kopya** çıkarıyor — ve bu kopya `Yonetici`'nin
eklediği alanları taşımıyor, yalnız `Calisan`'ın tanımladığı kısmı.

#### Aşırı Yükleme Seçimi Statik Tipe Göre, Nesne Kimliği Değişmiyor

`bilgi` işlevi hem `Calisan&` hem `Yonetici&` için aşırı yüklenmiş. Aynı `y` nesnesi
bir kez `Yonetici&` olarak, bir kez `Calisan&` bir referans üzerinden `bilgi`'ye
geçirildiğinde, hangi aşırı yükleme seçiliyor — ve ikisi de aynı nesneyle mi çalışıyor?

```cpp
// kalitim_asiri_yukleme_karsilastirmasi.cpp — asiri yukleme secimi statik tipe gore, nesne kimligi calisma zamaninda sabit
#include <iostream>
#include <string>

class Calisan {
public:
    explicit Calisan(std::string ad) : ad_(std::move(ad)) {}
    std::string ad_;
};

class Yonetici : public Calisan {
public:
    Yonetici(std::string ad, int takim_no) : Calisan(std::move(ad)), takim_no_(takim_no) {}
    int takim_no_;
};

void bilgi(const Calisan& c) {
    std::cout << "bilgi(Calisan&) cagrildi, ad " << c.ad_ << "\n";
}

void bilgi(const Yonetici& y) {
    std::cout << "bilgi(Yonetici&) cagrildi, ad " << y.ad_ << ", takim " << y.takim_no_ << "\n";
}

int main() {
    Yonetici y("Kaan", 3);
    Calisan& taban_referans = y;

    bilgi(y);
    bilgi(taban_referans);

    std::cout << std::boolalpha
              << "iki cagrinin da ayni y nesnesiyle calisip calismadigi "
              << (static_cast<const void*>(&y) == static_cast<const void*>(&taban_referans)) << "\n";

    return 0;
}
```

```
bilgi(Yonetici&) cagrildi, ad Kaan, takim 3
bilgi(Calisan&) cagrildi, ad Kaan
iki cagrinin da ayni y nesnesiyle calisip calismadigi true
```

**CB22.** `bilgi(y)` derleme zamanında `y`'nin statik tipine (`Yonetici`) bakarak
`bilgi(Yonetici&)`'yi seçiyor; `bilgi(taban_referans)` ise `taban_referans`'ın statik
tipine (`Calisan&`) bakarak `bilgi(Calisan&)`'ı seçiyor — ikisi de `virtual` değil, seçim
tamamen **derleme zamanında**, ifadenin statik tipine göre yapılıyor. Bu, M08/K02'de
ölçülen soruyla aynı eksende: hangi aşırı yüklemenin seçileceği bir **derleme zamanı**
kararı. Ama bu dersin ölçtüğü soru başka: seçilen işlev **hangi nesneyle** çalışıyor?
Adres karşılaştırması **true** — `bilgi(Yonetici&)`'nin çalıştığı `y` ile
`bilgi(Calisan&)`'ın çalıştığı `taban_referans`'ın gösterdiği nesne tamamen aynı.
Aşırı yükleme seçimi hangi **işlevin** çağrılacağını statik tipe göre belirliyor;
nesne kimliği ise bu seçimden bağımsız olarak hep aynı kalıyor. Sınıf **parça**: iki
farklı aşırı yükleme, aynı nesnenin taban parçasında çalışıyor.

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

Bu derste ölçülen kalıtım **tekli** ve **sanal olmayan**: `Yonetici` tek bir taban
sınıftan türedi ve hiçbir işlev `virtual` değildi, dolayısıyla taban alt nesnesinin
`y`'nin başında durduğu ve adresinin `y` ile çakıştığı gözlem bu koşullara bağlı —
çoklu kalıtımda (Çoklu Kalıtım ve Elmas Sorunu dersi) ikinci ve sonraki taban alt
nesneleri farklı bir ofsette durabilir, adresleri `y` ile çakışmayabilir. Ölçüm ayrıca
`Calisan`'ın `Yonetici` olmadan, kendi başına var olduğu durumları kapsamıyor — yalnız
bir `Yonetici` nesnesinin içindeki `Calisan` parçasını ölçtü. Bir taban işaretçisi ya da
referansı üzerinden yapılan bir çağrının hangi işlevi çalıştıracağı — çağrının statik
tipe mi, gerçek tipe mi baktığı — bu dersin konusu değil; bir sonraki ders bunu ölçüyor.

#### Özet

- Bir `Yonetici` nesnesi kurulduğunda önce `Calisan` yapıcısı, sonra `Yonetici`'nin
  kendi gövdesi çalışıyor — iki ayrı nesnenin kurulması değil, tek nesnenin iki aşaması.
- `Calisan* taban = &y` bir kopya çıkarmıyor; `taban` ile `y` aynı adresi taşıyor,
  çünkü tekli, sanal olmayan kalıtımda taban alt nesne türetilmiş nesnenin başında
  duruyor.
- Bir `Calisan&` parametresine `y` geçirmek de aynı adresi bağlıyor — referans bağlama
  bir kopya kurmuyor, sınıf **parça**.
- `sizeof(Yonetici)`, `Calisan` alt nesnesinin tam boyutunu ve `Yonetici`'nin kendi
  eklediği alanları birlikte taşıyor; taban alt nesne ayrı bir kutuda değil, aynı
  nesnenin fiziksel bir bölümünde duruyor.
- Yıkım sırası kurmanın tersini izliyor: önce `Yonetici`'nin kendi yıkıcısı, sonra
  `Calisan`'ınki — yine tek nesnenin iki aşamalı çözülüşü.

#### Sonraki Adım

Bu derste taban alt nesnenin adresi hep `y` ile çakıştı, çünkü hiçbir işlev `virtual`
değildi ve çağrılacak işlev derleme zamanında, ifadenin **statik tipine** bakılarak
belirlendi. Bir sonraki ders, Sanal Fonksiyonlar ve Sanal Tablolar, bir taban işaretçisi
ya da referansı üzerinden yapılan bir çağrının hangi işlevi çalıştıracağını nesnenin
**gerçek** tipine göre nasıl belirlediğini ölçüyor — ve bu kez ölçülen adres eşitliği
değil, aynı ifadenin çalışma zamanında hep aynı nesneyle çalıştığı.

### Sanal Fonksiyonlar ve Sanal Tablolar

Source: https://academia.sh/tr/kurslar/cpp-temelleri/sanal-fonksiyonlar-ve-sanal-tablolar

Bir taban işaretçisi ya da referansı üzerinden yapılan sanal çağrının, ifadenin statik tipinden bağımsız olarak hep aynı nesneyle çalıştığının ölçümü.

Önceki ders, taban alt nesnenin türetilmiş nesnenin bir **parçası** olduğunu, adresinin
onunla çakıştığını ölçtü — ama hiçbir işlev `virtual` değildi, çağrılacak işlev
derleme zamanında ifadenin statik tipine bakılarak belirlendi. Bu ders `virtual`
anahtar sözcüğünü ekliyor: artık bir `Sekil*` ya da `Sekil&` üzerinden yapılan bir
çağrı, işaretçinin statik tipine değil, **gösterdiği nesnenin gerçek tipine** bakarak
hangi işlevi çalıştıracağına karar veriyor. Soru değişti: taban alt nesnenin adresi
değil, sanal bir çağrının gerçekte **hangi nesneyle** çalıştığı ölçülüyor.

#### Sanal Çağrı Aynı Nesneye Ulaşıyor

`Dikdortgen`, `Sekil`'in `tanit` işlevini yeniden tanımlıyor. `d` nesnesine hem bir
`Sekil*` işaretçiyle hem bir `Sekil&` referansla erişilip `tanit` çağrıldığında, ikisi de
`d`'nin kendi işlevine mi ulaşıyor?

```cpp
// sanal_ayni_nesne.cpp — sanal cagri statik tipten bagimsiz olarak ayni nesneye ulasiyor
#include <iostream>

class Sekil {
public:
    virtual ~Sekil() = default;
    virtual void tanit(const void* beklenen) const {
        std::cout << "tanit(Sekil): calisan nesne beklenenle ayni mi "
                  << std::boolalpha << (static_cast<const void*>(this) == beklenen) << "\n";
    }
};

class Dikdortgen : public Sekil {
public:
    void tanit(const void* beklenen) const override {
        std::cout << "tanit(Dikdortgen): calisan nesne beklenenle ayni mi "
                  << std::boolalpha << (static_cast<const void*>(this) == beklenen) << "\n";
    }
};

int main() {
    Dikdortgen d;
    Sekil* taban_isaretci = &d;
    Sekil& taban_referans = d;

    taban_isaretci->tanit(&d);
    taban_referans.tanit(&d);

    return 0;
}
```

```
tanit(Dikdortgen): calisan nesne beklenenle ayni mi true
tanit(Dikdortgen): calisan nesne beklenenle ayni mi true
```

**CB4.** İki çağrı da statik tipi `Sekil` olan bir ifade üzerinden yapıldı
(`taban_isaretci`, `taban_referans`), ama ikisi de "tanit(Dikdortgen)" bastı — sanal
çağrı, işaretçinin/referansın statik tipine değil, gösterdiği nesnenin gerçek tipine
bakarak `Dikdortgen::tanit`'i seçti. İşlev içinde `this`, `d`'nin adresiyle
karşılaştırıldığında **true** dönüyor: çağrı yalnız doğru işlevi seçmekle kalmıyor,
seçtiği işlevi de tam olarak `d`'nin kendisi üzerinde çalıştırıyor, bir kopya ya da
başka bir nesne üzerinde değil. (M08/K02'de sorulan soru çağrıyı hangi tipin
çözdüğüydü; burada da benzer bir mekanizma var, ama ölçülen şey çağrının **hangi
nesneyle** çalıştığı — ve Java'da nesne dilimleme yokken C++'ta taban tipe değerle
atama yeni ve eksik bir nesne kurardı; burada yalnız işaretçi/referans erişimi var,
dilimleme devreye girmiyor.) M11/K03'ün Nesne Benzeri Tasarım dersinde elle kurulan
yöntem tablosu (işlev işaretçileri taşıyan bir struct) burada derleyicinin ürettiği
sanal işlev tablosuna karşılık geliyor; mekanizmanın ayrıntısı orada işlendi, burada yeniden
anlatılmıyor. Sınıf **kendisi**: `this`, çağrının yapıldığı ifadenin statik tipinden
bağımsız olarak hep `d`'nin adresini taşıyor.

#### Taban İşaretçi Dizisi: Her Çağrı Kendi Nesnesine Ulaşıyor

Bir `Sekil*` dizisi farklı türetilmiş nesnelere işaret ettiğinde, her eleman kendi
gösterdiği nesneye mi ulaşıyor, yoksa aralarında bir karışma mı oluyor?

```cpp
// sanal_dizi.cpp — dizideki her taban isaretcisi kendi gosterdigi nesneye ulasiyor
#include <iostream>

class Sekil {
public:
    virtual ~Sekil() = default;
    virtual bool ayni_mi(const void* adres) const {
        return static_cast<const void*>(this) == adres;
    }
};

class Dikdortgen : public Sekil {};
class Ucgen : public Sekil {};

int main() {
    Dikdortgen d;
    Ucgen u;
    Sekil* sekiller[2] = { &d, &u };

    std::cout << std::boolalpha;
    std::cout << "sekiller[0] kendi nesnesi d ile ayni mi "
              << sekiller[0]->ayni_mi(&d) << "\n";
    std::cout << "sekiller[1] kendi nesnesi u ile ayni mi "
              << sekiller[1]->ayni_mi(&u) << "\n";
    std::cout << "sekiller[0] u ile ayni mi (capraz denetim) "
              << sekiller[0]->ayni_mi(&u) << "\n";

    return 0;
}
```

```
sekiller[0] kendi nesnesi d ile ayni mi true
sekiller[1] kendi nesnesi u ile ayni mi true
sekiller[0] u ile ayni mi (capraz denetim) false
```

**CB5.** `Dikdortgen` ve `Ucgen` `tanit`'i yeniden tanımlamıyor, `Sekil::ayni_mi`'yi
olduğu gibi devralıyor — ama işlev yine de her çağrıldığında `this`'i çağrıldığı
elemanın gösterdiği gerçek nesneye bağlıyor. `sekiller[0]->ayni_mi(&d)` **true**,
`sekiller[1]->ayni_mi(&u)` **true**: her eleman kendi nesnesine ulaşıyor. Çapraz
denetim bunu daha da netleştiriyor — `sekiller[0]` (`d`'yi gösteren eleman) `&u` ile
karşılaştırıldığında **false** dönüyor, çünkü `sekiller[0]`'ın çalıştığı nesne `d`,
`u` değil. Sınıf **kendisi**: dizideki her taban işaretçisi, aynı taban tipini taşısa
da, kendi gösterdiği tikel nesneden başka hiçbir nesneyle çalışmıyor.

#### İşleve Referansla Geçirmek Nesneyi Değiştirmiyor

Bir işleve `const Sekil&` parametresiyle geçirilen bir nesne, işlev içinde hâlâ
dışarıdaki aynı nesne mi, yoksa işlevin kendi kopyası mı?

```cpp
// sanal_referans_parametre.cpp — isleve referansla gecirilen nesne disaridakiyle ayni
#include <iostream>

class Sekil {
public:
    virtual ~Sekil() = default;
    virtual bool ayni_mi(const void* adres) const {
        return static_cast<const void*>(this) == adres;
    }
};

class Dikdortgen : public Sekil {};

void dogrula(const Sekil& s, const void* beklenen) {
    std::cout << std::boolalpha
              << "islev icinde cagrilan nesne beklenenle ayni mi "
              << s.ayni_mi(beklenen) << "\n";
}

int main() {
    Dikdortgen d;
    dogrula(d, &d);
    return 0;
}
```

```
islev icinde cagrilan nesne beklenenle ayni mi true
```

**CB6.** `dogrula(d, &d)` çağrısında `s`, `d`'nin bir kopyası değil, `d`'nin kendisine
bağlanan bir referans — `const Sekil&` parametre türü bunu bir kopya kurmadan
sağlıyor. İşlev içinde `s.ayni_mi(&d)` çağrıldığında sanal çağrı yine `Sekil::ayni_mi`'yi
seçiyor (çünkü `Dikdortgen` onu yeniden tanımlamadı) ve `this`, dışarıdaki `d`'nin
adresiyle karşılaştırıldığında **true** dönüyor. İşlev sınırını geçmek — parametrenin
referans olması koşuluyla — nesnenin kimliğini değiştirmiyor: `dogrula` içindeki `s`,
`main` içindeki `d` ile aynı nesne. Sınıf **kendisi**: işlev çağrısı bir kopya
kurmadığı sürece, sanal çağrı hep çağrının başladığı yerdeki gerçek nesneyle çalışıyor.

#### override ve final: Aynı Nesneye Yine de Gerçek Tipe Göre Ulaşmak

`KenarliUcgen`, `Ucgen`'den türeyen ve `ayni_mi`'yi hem `override` hem `final` olarak
işaretleyen, kendisi de `final` olan bir sınıf. Bu iki anahtar sözcük çalışma zamanı davranışını değil, derleme
zamanı denetimini etkiliyor — ama sonuçta çalışan sanal çağrı yine aynı nesneye mi
ulaşıyor?

```cpp
// sanal_override_final.cpp — override ve final ile isaretlenmis sanal cagri yine nesnenin gercek tipine gore calisiyor
#include <iostream>

class Sekil {
public:
    virtual ~Sekil() = default;
    virtual bool ayni_mi(const void* adres) const {
        return static_cast<const void*>(this) == adres;
    }
};

class Ucgen : public Sekil {
public:
    bool ayni_mi(const void* adres) const override {
        return static_cast<const void*>(this) == adres;
    }
};

class KenarliUcgen final : public Ucgen {
public:
    bool ayni_mi(const void* adres) const override final {
        return static_cast<const void*>(this) == adres;
    }
};

int main() {
    KenarliUcgen k;
    Sekil& taban_referans = k;

    std::cout << std::boolalpha
              << "taban referans uzerinden cagrilan ayni_mi beklenenle ayni mi "
              << taban_referans.ayni_mi(&k) << "\n";

    return 0;
}
```

```
taban referans uzerinden cagrilan ayni_mi beklenenle ayni mi true
```

**CB23.** `override`, derleyiciye "bu işlev bir taban sınıf işlevini yeniden
tanımlıyor" der ve imza eşleşmezse (örneğin `const` unutulursa, ya da parametre tipi
yanlış yazılırsa) derleme hatası çıkarır — imza kazara uyuşmayıp sessizce yeni, ayrı bir
işlev tanımlanmasını (ve taban işlevin hiç yeniden tanımlanmamasını) önler. Bu koruma
derleme zamanında işliyor; sandbox yalnız başarıyla derlenen kodu çalıştırdığından,
imza uyuşmazlığının nasıl bir hataya yol açtığı burada çalıştırılarak gösterilemiyor,
yalnız anlatılabiliyor. `final`, hem `KenarliUcgen` sınıfının hem `ayni_mi` işlevinin
daha fazla türetilmesini/yeniden tanımlanmasını derleme zamanında engelliyor — bu da
çalıştırılarak değil, yalnızca yasaklanan bir ihlalin derlenmemesiyle gözlenebilecek bir
şey. Çalışan kısım değişmedi: `taban_referans.ayni_mi(&k)` **true** basıyor, çünkü
`override`/`final` çalışma zamanı davranışını değiştirmiyor, yalnız hangi imzaların
geçerli sayılacağını daraltıyor. Sınıf **kendisi**: `final` bir sınıfın nesnesi de,
diğerleri gibi, sanal çağrının ulaştığı nesnenin ta kendisi.

#### Sanal Yıkıcı: Taban İşaretçiyle Silme Doğru Zinciri Çalıştırıyor

`Sekil`'in yıkıcısı `virtual`. `new` ile kurulan bir `Dikdortgen` bir `Sekil*`
üzerinden `delete` edildiğinde, yıkım `Dikdortgen`'in kendi yıkıcısından mı başlıyor,
yoksa doğrudan `Sekil`'inkine mi atlıyor?

```cpp
// sanal_yikici_taban_isaretciyle_silme.cpp — sanal yikici taban isaretciyle silindiginde dogru yikim zincirini calistiriyor
#include <iostream>

class Sekil {
public:
    virtual ~Sekil() { std::cout << "Sekil yikici\n"; }
};

class Dikdortgen : public Sekil {
public:
    ~Dikdortgen() override { std::cout << "Dikdortgen yikici\n"; }
};

int main() {
    Sekil* taban_isaretci = new Dikdortgen();
    std::cout << "-- silme basliyor --\n";
    delete taban_isaretci;
    return 0;
}
```

```
-- silme basliyor --
Dikdortgen yikici
Sekil yikici
```

**CB24.** `taban_isaretci`'nin statik tipi `Sekil*`, ama `delete taban_isaretci`
çağrıldığında önce **Dikdortgen yikici** basılıyor, sonra **Sekil yikici** — yıkım da,
kurma gibi, en türemişten en tabana doğru işliyor, ve bunu doğru sırayla başlatabilen
şey `~Sekil()`'in `virtual` olması. Bu, standartın tanımladığı davranış: taban sınıfın
yıkıcısı sanal olduğunda, taban işaretçi üzerinden yapılan `delete` gerçek (dinamik)
tipin yıkıcısını çağırarak başlıyor. Karşılığının ise tanımlı bir sonucu yok: `~Sekil()`
`virtual` olmasaydı, statik ile dinamik tipin farklı olduğu bu `delete taban_isaretci`
standarda göre **tanımsız davranış** olurdu. Yaygın derleyicilerde bu çoğu zaman yalnız
`Sekil`'in yıkıcısının çalışması ve `Dikdortgen`'in eklediği kaynakların serbest
bırakılmaması biçiminde görülür, ama bu bir derleyici davranışıdır, dilin güvencesi
değildir. Bu dal burada çalıştırılmıyor: tanımsız davranış deterministik
bir çıktı vermeyeceğinden sandbox'ta doğrulanabilir bir sonucu yok, yalnız anlatılabilir.
Sınıf **kendisi**: `delete` çağrısı `taban_isaretci`'nin statik tipinden bağımsız olarak
hep `d`'nin (gerçek nesnenin) tam yıkım zincirini işletiyor.

#### Elle Kurulan Yöntem Tablosu ile Derleyicinin Sanal İşlev Tablosu Aynı Sonucu Veriyor

M11/K03'ün Nesne Benzeri Tasarım dersi, işlev işaretçileri taşıyan bir `struct`
ile elle bir "yöntem tablosu" kurmuştu. Böyle elle kurulan bir tablo üzerinden yapılan
çağrı ile derleyicinin ürettiği sanal işlev tablosu üzerinden yapılan `virtual` çağrı, aynı
nesne için aynı sonucu mu veriyor?

```cpp
// sanal_el_ile_yontem_tablosu.cpp — elle kurulan yontem tablosu derleyicinin urettigi sanal islev tablosuyla ayni sonucu veriyor
#include <iostream>

struct Sekil {
    virtual ~Sekil() = default;
    virtual int alan() const { return 0; }
};

struct Kare : public Sekil {
    explicit Kare(int kenar) : kenar_(kenar) {}
    int alan() const override { return kenar_ * kenar_; }
    int kenar_;
};

struct ElleYontemTablosu {
    int (*alan)(const void*);
};

int kare_alan_el_ile(const void* nesne) {
    return static_cast<const Kare*>(nesne)->alan();
}

int main() {
    Kare kare(5);
    Sekil& sanal_erisim = kare;

    ElleYontemTablosu tablo{ kare_alan_el_ile };

    std::cout << "derleyicinin sanal cagrisi: " << sanal_erisim.alan() << "\n";
    std::cout << "elle kurulan tablo cagrisi: " << tablo.alan(&kare) << "\n";
    std::cout << std::boolalpha << "iki sonuc ayni mi "
              << (sanal_erisim.alan() == tablo.alan(&kare)) << "\n";

    return 0;
}
```

```
derleyicinin sanal cagrisi: 25
elle kurulan tablo cagrisi: 25
iki sonuc ayni mi true
```

**CB25.** `sanal_erisim.alan()` derleyicinin ürettiği sanal işlev tablosu üzerinden
`Kare::alan`'a ulaşıyor; `tablo.alan(&kare)` ise elle kurulan `ElleYontemTablosu`
üzerinden aynı işleve (bu kez doğrudan bir işlev işaretçisiyle) ulaşıyor. İkisi de
**25** basıyor ve karşılaştırma **true**: iki farklı mekanizma, aynı nesne için aynı
işlevi seçiyor. Fark, seçimin nasıl yapıldığında — sanal çağrıda derleyici her
nesneye gizli bir sanal işlev tablosu işaretçisi ekliyor ve `alan()` her çağrıldığında o
işaretçi üzerinden doğru girdiyi buluyor; elle kurulan tabloda bu adımı burada
`ElleYontemTablosu tablo{...}` satırı ve tabloyu doğru nesneyle eşleştirme
sorumluluğu programcıya ait. M11/K03'te bu mekanizma ayrıntılı işlendi; burada yalnız
iki yaklaşımın aynı sonuca vardığı doğrulanıyor. Sınıf **kendisi**: her iki çağrı da
`kare`'nin kendisi üzerinde çalışıyor.

#### Sanal Dinamik Gönderimin Yapısal Maliyeti: Daha Büyük Nesne

`SanalNokta`, `SanalsizNokta` ile aynı iki `int` alanını taşıyor, tek farkla: bir sanal
yıkıcısı var. Aynı alanları taşıyan bu iki sınıf aynı boyutta mı, yoksa sanal işlev
taşımanın nesne başına bir yapısal bedeli mi var?

```cpp
// sanal_dinamik_gonderim_maliyeti.cpp — sanal islev tasiyan sinif ayni alanlari tasiyan sanal olmayan siniftan daha buyuk
#include <iostream>

class SanalsizNokta {
public:
    int x_ = 0;
    int y_ = 0;
};

class SanalNokta {
public:
    virtual ~SanalNokta() = default;
    int x_ = 0;
    int y_ = 0;
};

int main() {
    std::cout << std::boolalpha
              << "sanal islev tasiyan sinif, tasimayandan daha buyuk mu "
              << (sizeof(SanalNokta) > sizeof(SanalsizNokta)) << "\n";
    return 0;
}
```

```
sanal islev tasiyan sinif, tasimayandan daha buyuk mu true
```

**CB26.** İki sınıf birebir aynı iki `int` alanını taşıyor, ama karşılaştırma **true**:
`SanalNokta` daha büyük. Fark, `virtual` bir işlev tanımlanır tanımlanmaz derleyicinin
her nesneye eklediği gizli bir sanal işlev tablosu işaretçisinden geliyor — bu, dinamik
gönderimin **nesne başına, bir kerelik** yapısal bedeli. Bunun yanında bir de **çağrı
başına** bir bedel var: her sanal çağrı, doğrudan bir atlama yerine önce nesnenin sanal
işlev tablosu işaretçisi üzerinden doğru girdiyi bulan bir dolaylama yapıyor. Bu ikinci bedel
burada ölçülmüyor, çünkü süre ölçümü donanıma, derleyici optimizasyon düzeyine ve
sandbox'ın kendi yüküne bağlı olur — bu ders bu tür ortama bağlı verileri raporlamıyor.
Ölçülebilir ve deterministik olan yalnız boyut farkı. Sınıf **parça**: sanal işlev tablosu
işaretçisi nesnenin bir parçası, ayrı bir nesne değil.

#### Yapıcı İçinde Sanal Çağrı: Türemişe Değil, Tabana Gidiyor

`Taban`'ın yapıcısı kendi gövdesi içinde `tanimla()`'yı çağırıyor; `Turemis` bu işlevi
yeniden tanımlıyor. Bir `Turemis` nesnesi kurulurken `Taban`'ın yapıcısı içindeki bu
çağrı `Turemis::tanimla`'ya mı ulaşıyor, yoksa `Taban::tanimla`'ya mı?

```cpp
// sanal_yapici_icinde_cagri.cpp — yapici icinde yapilan sanal cagri turemisin degil tabanin kendi islevine gidiyor
#include <iostream>

class Taban {
public:
    Taban() {
        std::cout << "Taban yapicisi icinde tanimla() cagriliyor\n";
        tanimla();
    }
    virtual void tanimla() const {
        std::cout << "Taban::tanimla calisti\n";
    }
};

class Turemis : public Taban {
public:
    Turemis() {
        std::cout << "Turemis yapicisi icinde tanimla() cagriliyor\n";
        tanimla();
    }
    void tanimla() const override {
        std::cout << "Turemis::tanimla calisti\n";
    }
};

int main() {
    Turemis t;
    return 0;
}
```

```
Taban yapicisi icinde tanimla() cagriliyor
Taban::tanimla calisti
Turemis yapicisi icinde tanimla() cagriliyor
Turemis::tanimla calisti
```

**CB27.** `t`'nin son hâli bir `Turemis`, ama `Taban`'ın yapıcısı çalışırken içindeki
`tanimla()` çağrısı **Taban::tanimla**'yı çalıştırıyor, `Turemis::tanimla`'yı değil.
Neden: `Taban`'ın yapıcısı çalıştığı sırada `Turemis`'in kendi parçası henüz
kurulmamış — nesnenin o andaki etkin tipi, sanal gönderim açısından, hâlâ `Taban`. Bu
zincir yukarı doğru ilerledikçe değişiyor: `Turemis`'in kendi yapıcısı çalışırken
yapılan `tanimla()` çağrısı artık **Turemis::tanimla**'yı çalıştırıyor, çünkü o noktada
nesnenin `Turemis` parçası da kurulmuş durumda. Sınıf **kendisi**: her iki çağrı da
`t`'nin ta kendisi üzerinde çalışıyor, ama hangi işlevin seçileceği, çağrının yapıldığı
anda inşa zincirinin nerede olduğuna bağlı.

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

Bu derste ölçülen sanal çağrılar hep bir işaretçi ya da referans üzerinden yapıldı;
`Dikdortgen`'i **değerle** bir `Sekil` değişkenine atamak (`Sekil s = d;`) bu ölçümün
kapsamı dışında — böyle bir atama sanal çağrıyı değil, bir önceki dersin sonunda
değinilen dilimlemeyi devreye sokar ve sonuç yeni, eksik bir `Sekil` nesnesidir. Ölçüm
ayrıca `tanit`'in ve `ayni_mi`'nin kendi içinde başka bir sanal çağrı yapmadığı basit
durumları kapsıyor; bir taban yıkıcının sanal olmaması durumunda ortaya çıkabilecek
eksik yıkım bu derste hiç gözlenmedi, çünkü her iki sınıf da (`Sekil`, türetilenleri)
sanal bir yıkıcı taşıyor. Bir taban sınıfın hiç gövdesi olmayan, yalnız arayüz sunan
işlevlerle kurulması — soyut sınıflar — bu dersin konusu değil; bir sonraki ders bunu
ölçüyor.

#### Özet

- Bir `Sekil*` ya da `Sekil&` üzerinden yapılan sanal çağrı, ifadenin statik tipine
  değil, gösterdiği nesnenin gerçek tipine bakarak hangi işlevi çalıştıracağına karar
  veriyor.
- Seçilen işlev içinde `this`, çağrının yapıldığı taban tipinden ifadeden bağımsız
  olarak hep aynı gerçek nesnenin adresini taşıyor — sınıf **kendisi**.
- Bir `Sekil*` dizisindeki her eleman kendi gösterdiği tikel nesneye ulaşıyor; çapraz
  denetim başka bir elemanın nesnesiyle eşleşmediğini doğruluyor.
- Bir işleve `const Sekil&` parametresiyle geçirilen nesne, işlev içinde de dışarıdaki
  aynı nesne — referans bağlama bir kopya kurmuyor.
- Taban tipe **değerle** atama (dilimleme) bu dersin kapsamı dışında; sanal çağrı yalnız
  işaretçi/referans erişiminde bu şekilde çalışıyor.

#### Sonraki Adım

Bu derste `Sekil::tanit` ve `Sekil::ayni_mi`'nin bir gövdesi vardı — türetilmiş sınıflar
onu yeniden tanımlamasa da çağrılabiliyordu. Bir sonraki ders, Soyut Sınıflar ve
Arayüzler, bir taban sınıfın hiç gövdesi olmayan, yalnız bir sözleşme bildiren
işlevlerle kurulduğu durumu ölçüyor — orada taban sınıftan doğrudan bir nesne
kurulamıyor, yalnız türetilmiş sınıflardan; ölçülen yine bir sanal çağrının hangi
nesneyle çalıştığı, ama bu kez taban tipin kendi başına hiçbir örneği yok.

### Soyut Sınıflar ve Arayüzler

Source: https://academia.sh/tr/kurslar/cpp-temelleri/soyut-siniflar-ve-arayuzler

Salt sanal bir işlevin taban sınıftan hiçbir örnek kurulamamasını nasıl garanti ettiğinin ve bir arayüzün, veri taşımayan salt sözleşmeden ibaret olduğunun ölçümü.

Önceki ders, `Sekil::tanit`'in bir gövdesi olduğunu ve türetilmiş sınıflar onu yeniden
tanımlamasa da çağrılabildiğini gösterdi. Bu ders, bir taban sınıfın hiç gövdesi
olmayan, yalnız bir sözleşme bildiren işlevlerle kurulduğu durumu ele alıyor: **salt
sanal işlev** (`= 0` ile işaretlenen). Böyle bir işlevi taşıyan sınıftan doğrudan nesne
kurulamıyor — yalnız onu **tam olarak** dolduran türetilmiş sınıflardan. Ölçü sorusu
yine aynı: bir taban işaretçisi ya da referansı üzerinden yapılan bir çağrı hangi
nesneyle çalışıyor — ama bu kez taban tipin kendi başına hiçbir örneği yok.

#### Taban Sınıftan Nesne Kurulamıyor, Yalnız Türetilmişlerden

`Araci`'nin `ayni_mi` işlevi salt sanal (`= 0`); bu, `Araci`'den doğrudan bir nesne
kurmayı derleme hatasına çeviriyor — `Araci a;` yazılamaz. Yalnız `ayni_mi`'yi dolduran
`Otomobil` ve `Bisiklet` gibi sınıflardan nesne kurulabiliyor. Bir `Araci*` dizisindeki
her eleman, yine kendi gösterdiği tikel nesneye mi ulaşıyor?

```cpp
// soyut_taban.cpp — salt sanal isleve sahip sinifin ornegi kurulamiyor, yalnizca dolduranlarin
#include <iostream>

class Araci {
public:
    virtual ~Araci() = default;
    virtual bool ayni_mi(const void* adres) const = 0;
};

class Otomobil : public Araci {
public:
    bool ayni_mi(const void* adres) const override {
        return static_cast<const void*>(this) == adres;
    }
};

class Bisiklet : public Araci {
public:
    bool ayni_mi(const void* adres) const override {
        return static_cast<const void*>(this) == adres;
    }
};

int main() {
    Otomobil o;
    Bisiklet b;
    Araci* araclar[2] = { &o, &b };

    std::cout << std::boolalpha;
    std::cout << "araclar[0] kendi nesnesi o ile ayni mi "
              << araclar[0]->ayni_mi(&o) << "\n";
    std::cout << "araclar[1] kendi nesnesi b ile ayni mi "
              << araclar[1]->ayni_mi(&b) << "\n";
    std::cout << "araclar[0] b ile ayni mi (capraz denetim) "
              << araclar[0]->ayni_mi(&b) << "\n";

    return 0;
}
```

```
araclar[0] kendi nesnesi o ile ayni mi true
araclar[1] kendi nesnesi b ile ayni mi true
araclar[0] b ile ayni mi (capraz denetim) false
```

**CB7.** `Araci a;` yazılsaydı derleyici reddederdi — salt sanal `ayni_mi` `Araci`'yi
tek başına eksik bırakıyor, yalnız `Otomobil` ve `Bisiklet` gibi işlevi tam dolduran
sınıflar kurulabilir. Bu yüzden bir `Araci*`'nin gösterebileceği tek şey, `ayni_mi`'yi
dolduran somut bir sınıfın örneğidir — taban sınıfın kendisine ait, "yedek" bir örneğe
düşme ihtimali hiç yok. `araclar[0]->ayni_mi(&o)` **true**, `araclar[1]->ayni_mi(&b)`
**true**, çapraz denetim **false**: her eleman kendi somut nesnesine ulaşıyor. (M08/K02'de
sorulan soru çağrıyı hangi tipin çözdüğüydü; burada taban tipin kendi başına hiç örneği
olmadığından çözüm hep somut bir türe düşüyor — ve Java'da bir arayüz referansı da hiçbir
zaman salt arayüz tipinde bir nesneye işaret edemez, C++'ta salt sanal işlev aynı
kısıtlamayı sağlıyor.) Sınıf **kendisi**: çağrı, taban tipin kendisi hiç var olamadığı
için, zorunlu olarak somut bir türetilmiş nesneyle çalışıyor.

#### Devralınan Gövde de Aynı Nesneyle Çalışıyor

`Araci`, salt sanal `ayni_mi`'nin yanında gövdeli bir `bakim_bilgisi` de taşıyor.
`Otomobil` bunu yeniden tanımlamıyor, `Bisiklet` tanımlıyor. Devralınan gövde
çalıştığında `this` yine çağrılan nesnenin adresini mi taşıyor?

```cpp
// soyut_govdeli_isliv.cpp — devralinan govdeli islev de cagrilan nesnenin adresini tasiyor
#include <iostream>

class Araci {
public:
    virtual ~Araci() = default;
    virtual bool ayni_mi(const void* adres) const = 0;
    virtual void bakim_bilgisi(const void* beklenen) const {
        std::cout << "Araci::bakim_bilgisi calisan nesne beklenenle ayni mi "
                  << std::boolalpha << (static_cast<const void*>(this) == beklenen) << "\n";
    }
};

class Otomobil : public Araci {
public:
    bool ayni_mi(const void* adres) const override {
        return static_cast<const void*>(this) == adres;
    }
};

class Bisiklet : public Araci {
public:
    bool ayni_mi(const void* adres) const override {
        return static_cast<const void*>(this) == adres;
    }
    void bakim_bilgisi(const void* beklenen) const override {
        std::cout << "Bisiklet::bakim_bilgisi calisan nesne beklenenle ayni mi "
                  << std::boolalpha << (static_cast<const void*>(this) == beklenen) << "\n";
    }
};

int main() {
    Otomobil o;
    Bisiklet b;
    Araci* a1 = &o;
    Araci* a2 = &b;

    a1->bakim_bilgisi(&o);
    a2->bakim_bilgisi(&b);

    return 0;
}
```

```
Araci::bakim_bilgisi calisan nesne beklenenle ayni mi true
Bisiklet::bakim_bilgisi calisan nesne beklenenle ayni mi true
```

**CB8.** `Otomobil`, `bakim_bilgisi`'yi yeniden tanımlamadığı için `a1->bakim_bilgisi(&o)`
çağrısı `Araci::bakim_bilgisi`'nin gövdesini çalıştırıyor — ama bu gövde içinde bile
`this`, `o`'nun adresiyle karşılaştırıldığında **true** dönüyor. `Bisiklet` kendi
gövdesini sağladığı için `a2->bakim_bilgisi(&b)` `Bisiklet::bakim_bilgisi`'ni çalıştırıyor,
`this` yine `b`'nin adresi. Devralınan bir gövdenin çalışması, `Araci`'nin kendi başına
bir örneğinin var olduğu anlamına gelmiyor — o gövde yalnız somut bir `Otomobil`
nesnesi üzerinde çalıştırılıyor. Sınıf **kendisi**: taban sınıfın sağladığı ortak gövde
bile, çağrıldığı somut nesneden başka hiçbir nesneyle çalışmıyor.

#### Bir Arayüz: Yalnızca Sözleşme, Ortak Veri Yok

`Araci` veri taşımıyordu ama ortak bir gövde taşıyordu — tam bir soyut sınıftı.
`Yazdirilabilir` bundan farklı: hiç veri alanı, hiç gövdeli işlev yok, yalnız bir
sözleşme. `Rapor` bu sözleşmeyi dolduruyor ve kendi verisini taşıyor.

```cpp
// arayuz_sozlesme.cpp — arayuz hic veri tasimiyor, sozlesmeyi dolduran kendi verisini tasiyor
#include <iostream>
#include <string>
#include <utility>

class Yazdirilabilir {
public:
    virtual ~Yazdirilabilir() = default;
    virtual bool ayni_mi(const void* adres) const = 0;
};

class Rapor : public Yazdirilabilir {
public:
    explicit Rapor(std::string baslik) : baslik_(std::move(baslik)) {}
    bool ayni_mi(const void* adres) const override {
        return static_cast<const void*>(this) == adres;
    }
    const std::string& baslik() const { return baslik_; }
private:
    std::string baslik_;
};

void listele(const Yazdirilabilir& y, const void* beklenen) {
    std::cout << std::boolalpha
              << "listele icinde cagrilan nesne beklenenle ayni mi "
              << y.ayni_mi(beklenen) << "\n";
}

int main() {
    Rapor r("Yillik Rapor");
    std::cout << "raporun basligi " << r.baslik() << "\n";
    listele(r, &r);
    return 0;
}
```

```
raporun basligi Yillik Rapor
listele icinde cagrilan nesne beklenenle ayni mi true
```

**CB9.** `Yazdirilabilir` hiçbir veri alanı bildirmiyor, `baslik_` tümüyle `Rapor`'a ait —
`Yazdirilabilir` bu veriye hiç karışmıyor, yalnız `ayni_mi` sözleşmesini dayatıyor. `r`,
`Yazdirilabilir&`'a bağlanıp `listele` içine geçirildiğinde `y.ayni_mi(&r)` yine **true**
dönüyor: referans bağlama burada da bir kopya kurmuyor, `y` `main` içindeki `r` ile aynı
nesne. `Araci` ile `Yazdirilabilir` arasındaki fark somut: `Araci` veri taşıyabilir ve
ortak gövdeli işlevler sunabilir (soyut sınıf), `Yazdirilabilir` yalnızca imzalardan
ibaret, hiç veri hiç gövde yok (arayüz). Sınıf **kendisi**: ikisinde de çağrı hep
sözleşmeyi dolduran somut nesneyle çalışıyor.

#### Salt Sanal İşlevin Gövdesi Olabilir, Ama Yalnız Nitelenmiş Çağrıyla

`= 0` bir işlevi salt sanal yapıyor ve taşıyan sınıfın örneklenmesini engelliyor — ama
bu, işlevin hiç gövdesi olamayacağı anlamına gelmiyor. `SesliArac::uyar` hem salt sanal
hem de bir gövdeye sahip. Bu gövdeye normal bir sanal çağrıyla mı, yoksa başka bir yolla
mı ulaşılıyor?

```cpp
// soyut_salt_sanalin_govdesi.cpp — salt sanal islevin govdesi olabilir, ama yalnizca nitelenmis cagriyla calisir
#include <iostream>

class SesliArac {
public:
    virtual ~SesliArac() = default;
    virtual void uyar() const = 0;
};

void SesliArac::uyar() const {
    std::cout << "SesliArac::uyar (varsayilan govde) calisti\n";
}

class Kamyon : public SesliArac {
public:
    void uyar() const override {
        std::cout << "Kamyon::uyar calisti, sonra taban govdeyi nitelenmis cagiriyor\n";
        SesliArac::uyar();
    }
};

int main() {
    Kamyon k;
    SesliArac& taban_referans = k;
    taban_referans.uyar();
    return 0;
}
```

```
Kamyon::uyar calisti, sonra taban govdeyi nitelenmis cagiriyor
SesliArac::uyar (varsayilan govde) calisti
```

**CB28.** `taban_referans.uyar()` sıradan bir sanal çağrı — ve `Kamyon` `uyar`'ı yeniden
tanımladığı için `SesliArac`'ın gövdesine kendiliğinden hiç ulaşmıyor, tıpkı önceki
derslerdeki her `override`'da olduğu gibi. `SesliArac::uyar`'ın gövdesi yalnız
`Kamyon::uyar`'ın içinden **nitelenmiş** bir çağrıyla (`SesliArac::uyar();`) elle
çağrıldığında çalışıyor — bu satır sanal gönderimi devre dışı bırakıp doğrudan o ismi
hedefliyor. "Salt sanal" ile "gövdesiz" burada ayrışıyor: `= 0`, sınıfın örneklenmesini
engelleyen ve türetilmiş sınıfları işlevi doldurmaya zorlayan şey; gövdenin olup
olmaması ayrı bir soru, ve varsa bu gövdeye yalnız nitelenmiş çağrıyla erişilebiliyor,
olağan sanal gönderimle değil. Sınıf **kendisi**: nitelenmiş çağrı bile `k`'nin
kendisi üzerinde çalışıyor, yalnız hangi gövdenin çalıştırılacağını isim çözümü
belirliyor.

#### Soyut Tabanın Örneği Kurulamıyor, Somut Taban Değerle Alınıp Dilimlenebiliyor

`Araci` salt sanal bir işlev taşıdığı için `Araci a;` hiç derlenmiyor — bu, `Araci`
tipinde bir **değer**in de hiçbir zaman var olamayacağı, dolayısıyla `Araci` için
nesne dilimleme riskinin bile oluşamayacağı anlamına geliyor. Peki soyut olmayan, gövdesi
tam bir taban sınıf söz konusu olduğunda durum nasıl değişiyor?

```cpp
// soyut_somut_taban_degerle_alma.cpp — soyut tabanin ornegi kurulamaz, somut taban ise degerle alinip dilimlenebilir
#include <iostream>
#include <string>

class TasitKaydi {
public:
    explicit TasitKaydi(std::string plaka) : plaka_(std::move(plaka)) {}
    std::string plaka() const { return plaka_; }
protected:
    std::string plaka_;
};

class AyrintiliKayit : public TasitKaydi {
public:
    AyrintiliKayit(std::string plaka, int km)
        : TasitKaydi(std::move(plaka)), km_(km) {}
    int km() const { return km_; }
private:
    int km_;
};

int main() {
    AyrintiliKayit ayrintili("34ABC123", 15000);
    TasitKaydi dilimlenmis = ayrintili;

    std::cout << std::boolalpha;
    std::cout << "dilimlenmis ile ayrintili'nin ayni adresi tasiyip tasimadigi "
              << (static_cast<void*>(&dilimlenmis) == static_cast<const void*>(&ayrintili)) << "\n";
    std::cout << "dilimlenmis nesnenin plakasi " << dilimlenmis.plaka() << "\n";

    return 0;
}
```

```
dilimlenmis ile ayrintili'nin ayni adresi tasiyip tasimadigi false
dilimlenmis nesnenin plakasi 34ABC123
```

**CB29.** `TasitKaydi` salt sanal işlev taşımıyor, somut bir sınıf — bu yüzden
`TasitKaydi dilimlenmis = ayrintili;` derleniyor ve Kalıtım dersindeki CB21'de ölçülen
dilimlemenin aynısını üretiyor: `dilimlenmis`, `ayrintili`'den bağımsız, kendi adresinde
yaşayan ayrı bir nesne (**false**), ve `AyrintiliKayit`'in eklediği `km_` alanı hiç
taşınmıyor. `Araci` için bu satırın bir karşılığı yok, çünkü `Araci` tipinde bir
değişken tanımlamanın kendisi derleme hatası — dilimlemenin hedefi olacak bir `Araci`
değeri hiç var olamıyor. Soyutluk, yalnız "yeniden tanımlama zorunluluğu" değil, aynı
zamanda "taban tipte hiçbir zaman bir değer oluşamaması" anlamına geliyor: soyut bir
taban, işaretçi ya da referansla erişilebilir ama asla bir değişkene, parametreye ya da
dönüş değerine değerle konulamaz — bu da onu dilimlemeye karşı yapısal olarak bağışık
kılıyor. Dilimleme riski yalnız somut sınıflar için var. Sınıf **kopya**: `dilimlenmis`,
`ayrintili`'nin taban parçasının bağımsız bir kopyası.

#### Arayüzün Rolü: Akraba Olmayan Tipleri Tek Sözleşme Altında İşlemek

`Rapor` ve `Fatura` birbirinden türemiyor, hiçbir veri ya da davranışı paylaşmıyorlar —
tek ortak noktaları `Yazdirilabilir` sözleşmesini doldurmaları. Bu ikisi aynı vektörde,
somut tipleri bilinmeden, tekdüze işlenebiliyor mu?

```cpp
// arayuz_rolu_coklu_tip.cpp — farkli somut tipler ayni arayuz uzerinden tekduze islenebiliyor
#include <iostream>
#include <string>
#include <vector>

class Yazdirilabilir {
public:
    virtual ~Yazdirilabilir() = default;
    virtual std::string metin() const = 0;
};

class Rapor : public Yazdirilabilir {
public:
    explicit Rapor(std::string baslik) : baslik_(std::move(baslik)) {}
    std::string metin() const override { return "Rapor: " + baslik_; }
private:
    std::string baslik_;
};

class Fatura : public Yazdirilabilir {
public:
    explicit Fatura(int tutar) : tutar_(tutar) {}
    std::string metin() const override { return "Fatura: " + std::to_string(tutar_); }
private:
    int tutar_;
};

int main() {
    Rapor r("Yillik Rapor");
    Fatura f(150);
    std::vector<Yazdirilabilir*> belgeler{ &r, &f };

    for (const auto* belge : belgeler) {
        std::cout << belge->metin() << "\n";
    }

    return 0;
}
```

```
Rapor: Yillik Rapor
Fatura: 150
```

**CB30.** `Rapor` ve `Fatura` aynı hiyerarşi ağacında akraba değiller, ortak hiçbir veri
alanı ya da gövdeli işlev paylaşmıyorlar — bağları yalnız ikisinin de `Yazdirilabilir`'i
doldurması. `belgeler` vektörü ikisini de tek bir `Yazdirilabilir*` tipi altında tutuyor
ve döngü, elemanın somut tipini hiç bilmeden `metin()`'i çağırıyor; her çağrı yine de
doğru somut tipin işlevine ulaşıyor: "Rapor: Yillik Rapor" ve "Fatura: 150". Bu,
arayüzün asıl rolünü gösteriyor: soyut sınıfın aksine, arayüz akrabalık kurmuyor, yalnız
imzaca bir sözleşme dayatıyor — sözleşmeyi dolduran her tip, aralarında başka hiçbir
ortaklık olmasa da aynı koleksiyonda, aynı döngüde işlenebiliyor. Sınıf **kendisi**:
her `metin()` çağrısı, döngüdeki elemanın işaret ettiği somut nesnenin ta kendisiyle
çalışıyor.

#### Arayüzün de Sanal Bir Yıkıcıya İhtiyacı Var

`Yazdirilabilir` yalnız sözleşme sunuyor gibi görünse de, `Rapor` gibi somut bir
türetilmişi `Yazdirilabilir*` üzerinden `delete` edebilmek için kendi yıkıcısının da
sanal olması gerekiyor. Bu ders bunu doğruluyor mu?

```cpp
// arayuz_sanal_yikici.cpp — arayuzun sanal yikicisi turetilmis nesnenin tam yikim zincirini calistiriyor
#include <iostream>

class Yazdirilabilir {
public:
    virtual ~Yazdirilabilir() { std::cout << "Yazdirilabilir yikici\n"; }
    virtual void yaz() const = 0;
};

class Rapor : public Yazdirilabilir {
public:
    ~Rapor() override { std::cout << "Rapor yikici\n"; }
    void yaz() const override { std::cout << "Rapor::yaz\n"; }
};

int main() {
    Yazdirilabilir* belge = new Rapor();
    belge->yaz();
    std::cout << "-- silme basliyor --\n";
    delete belge;
    return 0;
}
```

```
Rapor::yaz
-- silme basliyor --
Rapor yikici
Yazdirilabilir yikici
```

**CB31.** `belge`'nin statik tipi `Yazdirilabilir*`, ama `delete belge` önce **Rapor
yikici**'yi, sonra **Yazdirilabilir yikici**'yi çalıştırıyor — tıpkı Sanal Fonksiyonlar
dersindeki CB24'te ölçülen ilkenin aynısı, burada arayüz bağlamında. `Yazdirilabilir`
salt sanal işlevler sunan bir sözleşme olsa da, yıkıcısı `virtual` olmasaydı aynı
`delete belge` yalnız `Yazdirilabilir`'in kendi yıkıcısını çağırırdı, `Rapor`'un
kaynakları hiç serbest bırakılmadan — ve statik/dinamik tip farklıyken sanal olmayan bir
yıkıcı üzerinden silme tanımsız davranış olurdu. Bu dal, CB24'te olduğu gibi,
deterministik bir çıktı vermeyeceği için burada çalıştırılmıyor. Sonuç: "yalnız sözleşme
sunmak" bir sınıfı sanal yıkıcı gereksiniminden muaf tutmuyor — taban işaretçi üzerinden
polymorphic silme yapılacaksa, arayüz de dâhil, yıkıcı sanal olmalı. Sınıf **kendisi**:
yıkım zinciri hep `belge`'nin işaret ettiği gerçek `Rapor` nesnesi üzerinde işliyor.

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

Bu derste ölçülen soyutluk **tekli kalıtım** altında: her somut sınıf tek bir soyut
taban sınıfı ya da arayüzü dolduruyordu. Bir sınıfın birden çok taban sınıftan
türeyerek birden çok sözleşmeyi aynı anda doldurduğu durum — çoklu kalıtım — bu dersin
kapsamı dışında; bir sonraki ders bunu ölçüyor. Salt sanal bir işlevin gövdesi de
olabileceği yalnız tek bir örnekle (`SesliArac::uyar`) gösterildi; o gövdenin hangi
tasarımlarda gerçekten gerektiği genellenmedi. `Araci a;` gibi bir satırın
gerçekten derleme hatası verdiği bu derste çalıştırılarak gösterilmedi, çünkü doğrulama
yalnız başarıyla derlenen kodu çalıştırıyor — bu, dersin metninde belirtilen ama koşulan
kod dışında kalan bir olgu.

#### Özet

- Salt sanal bir işlev (`= 0`) taşıyan bir sınıftan doğrudan nesne kurulamıyor; yalnız
  o işlevi dolduran türetilmiş sınıflardan nesne kurulabiliyor.
- Bir `Araci*` dizisindeki her eleman kendi gösterdiği somut nesneye ulaşıyor, çünkü
  taban tipin kendi başına hiç örneği yok — sınıf **kendisi**.
- Taban sınıfın sağladığı devralınan, gövdeli bir işlev bile çağrıldığı somut nesneden
  başka hiçbir nesneyle çalışmıyor.
- Bir soyut sınıf (`Araci`) veri taşıyabilir ve ortak gövdeli işlevler sunabilir; bir
  arayüz (`Yazdirilabilir`) yalnızca imzalardan ibarettir, hiç veri hiç gövde taşımaz.
- Arayüz referansına geçirilen nesne, işlev içinde de dışarıdaki aynı nesne — referans
  bağlama burada da bir kopya kurmuyor.

#### Sonraki Adım

Bu derste her somut sınıf tek bir soyut taban sınıfı ya da arayüzü dolduruyordu; taban
alt nesne hep türetilmiş nesnenin başında durdu, tıpkı Kalıtım dersindeki gibi. Bir
sonraki ders, Çoklu Kalıtım ve Elmas Sorunu, bir sınıfın **birden çok** taban sınıftan
aynı anda türediği durumu ölçüyor — orada ikinci ve sonraki taban alt nesneler artık
türetilmiş nesnenin başında durmayabiliyor, adresleri türetilmiş nesnenin adresiyle
çakışmayabiliyor. Ölçülen yine bir ifadenin hangi nesneyle çalıştığı, ama bu kez taban
alt nesnenin türetilmiş nesnenin **hangi parçasında** durduğu da sorunun bir parçası.

### Çoklu Kalıtım ve Elmas Sorunu

Source: https://academia.sh/tr/kurslar/cpp-temelleri/coklu-kalitim-ve-elmas-sorunu

Çoklu kalıtımda ikinci taban alt nesnenin türetilmiş nesneyle aynı adresi paylaşmadığının ve elmas biçimli kalıtımda ortak atanın sanal kalıtım olmadan ikiye katlandığının ölçümü.

Önceki iki ders tekli kalıtımı ölçtü: bir türetilmiş nesnenin tek bir taban alt nesnesi
vardı ve o alt nesnenin adresi hep türetilmiş nesnenin adresiyle çakıştı. Bu ders bir
sınıfın **birden çok** taban sınıftan aynı anda türediği durumu ele alıyor. İlk taban
alt nesne yine türetilmiş nesnenin başında duruyor mu? İkincisi de aynı adresi mi
paylaşıyor? Ve iki taban sınıfın ortak bir atası varsa, o ata türetilmiş nesnenin
içinde kaç kez yer kaplıyor?

#### Çoklu Kalıtımda İkinci Taban Alt Nesnenin Adresi Farklı

`Triatletci`, birbiriyle ilişkisiz iki sınıftan türüyor: `Yuzucu` ve `Kosucu`. İlk taban
alt nesne türetilmiş nesnenin başında mı duruyor, ikincisi de aynı adresi mi
paylaşıyor?

```cpp
// coklu_kalitim_ofset.cpp — coklu kalitimda ilk taban ayni adreste, ikincisi degil
#include <iostream>

class Yuzucu {
public:
    int stil_ = 1;
};

class Kosucu {
public:
    int mesafe_ = 5;
};

class Triatletci : public Yuzucu, public Kosucu {
public:
    int toplam_puan_ = 0;
};

int main() {
    Triatletci t;

    std::cout << std::boolalpha;
    std::cout << "t ile ilk taban Yuzucu adresinin ayni olup olmadigi "
              << (static_cast<void*>(&t) == static_cast<void*>(static_cast<Yuzucu*>(&t))) << "\n";
    std::cout << "t ile ikinci taban Kosucu adresinin ayni olup olmadigi "
              << (static_cast<void*>(&t) == static_cast<void*>(static_cast<Kosucu*>(&t))) << "\n";

    return 0;
}
```

```
t ile ilk taban Yuzucu adresinin ayni olup olmadigi true
t ile ikinci taban Kosucu adresinin ayni olup olmadigi false
```

**CB10.** `Triatletci`, kalıtım listesinde önce `Yuzucu`'yu, sonra `Kosucu`'yu bildiriyor
— derleyici taban alt nesneleri bu sırayla yerleştiriyor. İlk taban alt nesne
(`Yuzucu`) `t`'nin başında duruyor, bu yüzden `&t` ile `static_cast<Yuzucu*>(&t)` aynı
adresi taşıyor, **true**. İkinci taban alt nesne (`Kosucu`) ise `Yuzucu` alt nesnesinden
sonraki bir ofsette duruyor; `&t` ile `static_cast<Kosucu*>(&t)` farklı adresler,
**false**. Tekli kalıtımda (Kalıtım dersi) taban alt nesnenin adresi hep türetilmiş
nesneyle çakışıyordu çünkü taşınacak tek bir taban vardı; çoklu kalıtımda bu yalnız
**ilk** taban için geçerli. Sınıf **parça**: her iki taban alt nesne de `t`'nin bir
parçası, ama aynı adresi paylaşan yalnız ilki.

#### Elmas Sorunu: Ortak Atadan İki Ayrı Kopya

`Yuzucu` ve `Kosucu`'nun ortak bir atası `Canli` olsun. `Triatletci` ikisinden birden
türediğinde, `Canli` alt nesnesine `Yuzucu` üzerinden ve `Kosucu` üzerinden ulaşmak aynı
nesneye mi çıkıyor, yoksa iki ayrı kopyaya mı?

```cpp
// elmas_ayrik.cpp — sanal kalitim olmadan ortak ata iki ayri kopyaya bolunuyor
#include <iostream>

class Canli {
public:
    int can_ = 100;
};

class Yuzucu : public Canli {};
class Kosucu : public Canli {};
class Triatletci : public Yuzucu, public Kosucu {};

int main() {
    Triatletci t;

    Canli* yuzucu_uzerinden = static_cast<Canli*>(static_cast<Yuzucu*>(&t));
    Canli* kosucu_uzerinden = static_cast<Canli*>(static_cast<Kosucu*>(&t));

    std::cout << std::boolalpha;
    std::cout << "Yuzucu uzerinden ve Kosucu uzerinden ulasilan Canli'nin ayni olup olmadigi "
              << (static_cast<void*>(yuzucu_uzerinden) == static_cast<void*>(kosucu_uzerinden)) << "\n";
    std::cout << "Triatletci boyutunun iki ayri Canli'yi barindiracak kadar buyuk olup olmadigi "
              << (sizeof(Triatletci) >= 2 * sizeof(Canli)) << "\n";

    return 0;
}
```

```
Yuzucu uzerinden ve Kosucu uzerinden ulasilan Canli'nin ayni olup olmadigi false
Triatletci boyutunun iki ayri Canli'yi barindiracak kadar buyuk olup olmadigi true
```

**CB11.** `Yuzucu` ve `Kosucu`, `Canli`'den **sıradan** (sanal olmayan) kalıtımla
türüyor; her biri kendi `Canli` alt nesnesini taşıyor. `Triatletci` her ikisinden
türediğinde, bu iki `Canli` kopyası da `Triatletci`'nin içine giriyor — birleşmiyor.
`yuzucu_uzerinden` ile `kosucu_uzerinden` farklı adresler taşıyor, **false**: bunlar iki
ayrı `Canli` nesnesi. `sizeof(Triatletci)`'nin en az iki `Canli` büyüklüğünü kapsaması
bunu bellek düzeninde doğruluyor, **true**. Bu, **elmas sorunu**: `t.can_` yazmak hangi
kopyanın kastedildiği belirsiz olduğu için derleyiciyi durdurur, yalnız `t.Yuzucu::can_`
ya da `t.Kosucu::can_` gibi açık bir yol belirtilirse derlenir — bu ders o belirsiz
satırı hiç çalıştırmıyor, yalnız iki kopyanın varlığını adres ve boyut ölçümüyle
gösteriyor. Sınıf **parça**: `Triatletci`, `Canli`'nin bir değil, iki ayrı parçasını
taşıyor.

#### Sanal Kalıtım: Tek Paylaşılan Taban Alt Nesnesi

`Yuzucu` ve `Kosucu`, `Canli`'den **sanal** kalıtımla türerse, `Triatletci` her ikisinden
türediğinde `Canli` alt nesnesi yine ikiye mi bölünüyor, yoksa tek bir paylaşılan
nesneye mi indiriliyor?

```cpp
// elmas_sanal.cpp — sanal kalitim ortak atayi tek paylasilan alt nesneye indiriyor
#include <iostream>

class Canli {
public:
    int can_ = 100;
};

class Yuzucu : virtual public Canli {};
class Kosucu : virtual public Canli {};
class Triatletci : public Yuzucu, public Kosucu {};

int main() {
    Triatletci t;

    Canli* yuzucu_uzerinden = static_cast<Canli*>(static_cast<Yuzucu*>(&t));
    Canli* kosucu_uzerinden = static_cast<Canli*>(static_cast<Kosucu*>(&t));

    std::cout << std::boolalpha;
    std::cout << "Yuzucu uzerinden ve Kosucu uzerinden ulasilan Canli'nin ayni olup olmadigi "
              << (static_cast<void*>(yuzucu_uzerinden) == static_cast<void*>(kosucu_uzerinden)) << "\n";
    std::cout << "t.can_ ile yuzucu_uzerinden->can_ 'in ayni adresi tasiyip tasimadigi "
              << (static_cast<void*>(&t.can_) == static_cast<void*>(&yuzucu_uzerinden->can_)) << "\n";

    return 0;
}
```

```
Yuzucu uzerinden ve Kosucu uzerinden ulasilan Canli'nin ayni olup olmadigi true
t.can_ ile yuzucu_uzerinden->can_ 'in ayni adresi tasiyip tasimadigi true
```

**CB12.** `virtual public Canli` bildirimi, `Yuzucu` ve `Kosucu`'ya "kendi `Canli`
kopyanı kurma, en tepedeki türetilmiş sınıfın paylaştığı tek kopyayı kullan" diyor.
`Triatletci` her ikisinden türediğinde artık tek bir `Canli` alt nesnesi var —
`yuzucu_uzerinden` ile `kosucu_uzerinden` aynı adresi taşıyor, **true**. Bu paylaşım
sayesinde `t.can_` de artık belirsiz değil, doğrudan derleniyor ve aynı paylaşılan
nesneye işaret ediyor, **true**. Önceki ölçümdeki elmas sorunu — iki ayrı `Canli`
kopyası — burada ortadan kalktı, çünkü `Canli`'ye giden iki yol da aynı tek nesnede
birleşiyor. Sınıf **parça**: `Triatletci`, `Canli`'nin artık iki değil, tek bir
parçasını taşıyor ve bu parçaya hem `Yuzucu` hem `Kosucu` üzerinden ulaşılıyor.

#### Nitelenmiş Adla İki Ayrı Kopyaya Erişip Yalnız Birini Değiştirmek

CB11, `t.can_` yazmanın hangi kopya kastedildiği belirsiz olduğu için derlenmediğini,
yalnız `t.Yuzucu::can_` ya da `t.Kosucu::can_` gibi nitelenmiş bir adla derlendiğini
söylemişti ama bu satırı hiç çalıştırmamıştı. Nitelenmiş adla yalnız bir kopya
değiştirildiğinde, diğer kopya gerçekten etkilenmeden mi kalıyor?

```cpp
// coklu_kalitim_nitelenmis_erisim.cpp — sanal olmayan kalitimda iki ayri Canli kopyasina nitelenmis adla erisilip biri degistiriliyor
#include <iostream>

class Canli {
public:
    int can_ = 100;
};

class Yuzucu : public Canli {};
class Kosucu : public Canli {};
class Triatletci : public Yuzucu, public Kosucu {};

int main() {
    Triatletci t;

    t.Yuzucu::can_ = 70;

    std::cout << "t.Yuzucu::can_ degistirildikten sonra degeri " << t.Yuzucu::can_ << "\n";
    std::cout << "t.Kosucu::can_ degismeden kalan degeri " << t.Kosucu::can_ << "\n";
    std::cout << std::boolalpha
              << "t.Yuzucu::can_ ile t.Kosucu::can_ 'in ayni adresi tasiyip tasimadigi "
              << (static_cast<void*>(&t.Yuzucu::can_) == static_cast<void*>(&t.Kosucu::can_)) << "\n";

    return 0;
}
```

```
t.Yuzucu::can_ degistirildikten sonra degeri 70
t.Kosucu::can_ degismeden kalan degeri 100
t.Yuzucu::can_ ile t.Kosucu::can_ 'in ayni adresi tasiyip tasimadigi false
```

**CB32.** `t.Yuzucu::can_ = 70;` nitelenmiş adı kullanarak derleyiciye hangi kopyanın
kastedildiğini açıkça söylüyor ve yalnız o kopyayı değiştiriyor. `t.Kosucu::can_`
sorgulandığında hâlâ ilk değeri, **100**, dönüyor — `Yuzucu` üzerinden yapılan değişiklik
`Kosucu`'nun kendi `Canli` kopyasına hiç sızmıyor. Adres karşılaştırması bunu
doğruluyor: **false**, iki nitelenmiş erişim gerçekten iki ayrı nesneye ulaşıyor. Bu,
CB11'in bellek boyutuyla dolaylı gösterdiği şeyi doğrudan kanıtlıyor: `Triatletci`'nin
içinde `Canli` bir kez değil, iki kez var ve nitelenmiş erişim programcıya bu iki
kopyadan hangisiyle çalışacağını seçme imkânı veriyor — derleyicinin kendiliğinden
seçemediği bir kararı elle vermek. Sınıf **parça**: `t.Yuzucu::can_` ve `t.Kosucu::can_`,
`t`'nin iki farklı parçası, bağımsız olarak değişebiliyorlar.

#### Sanal Tabanın Yapıcısını En Türemiş Sınıf Çağırıyor

`Canli`'nin artık parametre alan, varsayılan olmayan bir kurucusu var. `Yuzucu` ve
`Kosucu` kendi kurucularında `Canli`'yi kendi değerleriyle kurmaya çalışıyor, ama
`Triatletci` de kendi kurucusunda `Canli`'yi ayrı bir değerle kuruyor. Sanal kalıtımda
paylaşılan `can_` hangi kurucunun verdiği değeri taşıyor?

```cpp
// elmas_sanal_yapici_zinciri.cpp — sanal tabanin yapicisini en turemis sinif dogrudan cagiriyor, aradakiler gorunmezden geliniyor
#include <iostream>

class Canli {
public:
    explicit Canli(int can) : can_(can) {
        std::cout << "Canli yapicisi can=" << can_ << "\n";
    }
    int can_;
};

class Yuzucu : virtual public Canli {
public:
    Yuzucu() : Canli(1) {
        std::cout << "Yuzucu yapicisi (kendi Canli(1) cagrisi gorunmezden gelinecek)\n";
    }
};

class Kosucu : virtual public Canli {
public:
    Kosucu() : Canli(2) {
        std::cout << "Kosucu yapicisi (kendi Canli(2) cagrisi gorunmezden gelinecek)\n";
    }
};

class Triatletci : public Yuzucu, public Kosucu {
public:
    Triatletci() : Canli(100), Yuzucu(), Kosucu() {
        std::cout << "Triatletci yapicisi, paylasilan can_ = " << can_ << "\n";
    }
};

int main() {
    Triatletci t;
    return 0;
}
```

```
Canli yapicisi can=100
Yuzucu yapicisi (kendi Canli(1) cagrisi gorunmezden gelinecek)
Kosucu yapicisi (kendi Canli(2) cagrisi gorunmezden gelinecek)
Triatletci yapicisi, paylasilan can_ = 100
```

**CB33.** `Canli yapicisi can=100` yalnız **bir kez** basılıyor, `can=1` ya da `can=2`
hiç görünmüyor — sanal tabanın kurucusunu çağırma sorumluluğu, ara sınıflarda değil,
**en türemiş sınıfta**. `Yuzucu`'nun ve `Kosucu`'nun kendi mem-initializer listelerindeki
`Canli(1)` ve `Canli(2)` çağrıları, `Triatletci` üzerinden kurulurken gözardı ediliyor;
bu satırlar yalnız `Yuzucu` ya da `Kosucu` **tek başına** (`Triatletci`'siz) kurulduğunda
devreye girerdi. `Triatletci`'nin kendi mem-initializer listesindeki `Canli(100)` ise
gerçekten çalışan tek çağrı — bu yüzden paylaşılan `can_` **100**. Sanal tabanlar,
kalıtım zincirindeki konumlarından bağımsız olarak, her zaman en tepedeki türetilmiş
sınıfın kurucusu tarafından ilklendiriliyor; bu da CB12'nin ölçtüğü "tek paylaşılan
nesne" ilkesinin kurucu tarafındaki karşılığı — paylaşılan nesne tek olduğu için, onu
kuracak çağrı da tek olmak zorunda. Sınıf **parça**: `can_`, `t`'nin tek bir paylaşılan
parçası ve bu parçanın değerini yalnız en türemiş sınıfın kurucusu belirliyor.

#### Sanal Kalıtımın Yapısal Maliyeti: Kopya Azalıyor, İşaretçi Ekleniyor

CB11 sanal olmayan elmasta `Canli`'nin iki kez yer kapladığını, CB12 sanal elmasta tek
kopyaya indiğini göstermişti. Kopya sayısı azalırken toplam nesne boyutu da aynı oranda
mı küçülüyor, yoksa sanal kalıtımın kendi ek yükü bu kazancın bir kısmını mı yiyor?

```cpp
// elmas_sanal_yapisal_maliyet.cpp — sanal kalitimin nesne boyutuna etkisi ayni sinif kumesinin sanalsiz halidir karsilastiriliyor
#include <iostream>

class Canli {
public:
    int can_ = 1;
};

class YuzucuDuz : public Canli {};
class KosucuDuz : public Canli {};
class TriatletciDuz : public YuzucuDuz, public KosucuDuz {};

class YuzucuSanal : virtual public Canli {};
class KosucuSanal : virtual public Canli {};
class TriatletciSanal : public YuzucuSanal, public KosucuSanal {};

int main() {
    std::cout << std::boolalpha
              << "sanal elmasin boyutu sanalsiz elmastan kucuk mu: "
              << (sizeof(TriatletciSanal) < sizeof(TriatletciDuz)) << "\n";
    std::cout << "sanal elmasin boyutu tek bir Canli'den buyuk mu (ek yuk var mi): "
              << (sizeof(TriatletciSanal) > sizeof(Canli)) << "\n";
    std::cout << "sanalsiz elmasin boyutu en az iki Canli kadar mi: "
              << (sizeof(TriatletciDuz) >= 2 * sizeof(Canli)) << "\n";

    return 0;
}
```

```
sanal elmasin boyutu sanalsiz elmastan kucuk mu: false
sanal elmasin boyutu tek bir Canli'den buyuk mu (ek yuk var mi): true
sanalsiz elmasin boyutu en az iki Canli kadar mi: true
```

**CB34.** Beklenti kopya sayısı azaldığı için sanal elmasın daha küçük çıkması yönündeydi,
ama ölçüm bunu doğrulamıyor: **false**. `Canli`'nin tek bir `int` üyesi olduğu için
kopyanın bedeli zaten küçük (birkaç bayt); buna karşılık `Yuzucu` ve `Kosucu`'nun her
biri, kendi `Canli`'sine nasıl ulaşacağını çözmek için ayrı bir kayıt (sanal taban
işaretçisi ya da benzeri bir mekanizma, derleyiciye göre değişir) taşıyor — iki taban,
iki ayrı kayıt. Bu derleyicide ek kayıtların toplam bedeli, tasarruf edilen tek `Canli`
kopyasından daha ağır bastı. Sanal elmasın tek bir `Canli`'den büyük olması (**true**)
zaten bekleniyordu; asıl öğretici olan, "daha az kopya" ile "daha küçük nesne"nin aynı
şey olmadığıdır. Bu, CB26'nın sanal işlevler için ölçtüğü "vptr her nesneye ekleniyor"
bulgusunun çoklu kalıtım bağlamındaki karşılığı: paylaşılan bir şeyi (yöntem tablosu /
taban alt nesnesi) tek elde tutmak, nesneye küçük ama sıfır olmayan bir muhasebe
maliyeti yüklüyor — ve bu maliyet, ortadan kaldırılan kopyanın kendi boyutundan büyük
de olabiliyor. Sınıf **parça**: ölçülen büyüklük farkı, `t`'nin sanal taban alt
nesnesinin kendisinden değil, o alt nesneye giden yolu tutan ek parçalardan
kaynaklanıyor.

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

Bu derste ölçülen sanal kalıtım, verinin (`can_`) paylaşımını ve sanal taban sınıfın
kurucusunu yalnız en türemiş sınıfın çağırdığı kuralı gösterdi; ama ölçüm sanal
kalıtımın taşıdığı ek dolaylılık maliyetini (her erişimin bir sanal taban işaretçisi
üzerinden çözülmesi) ele almadı — yalnız adres eşitliğini ölçtü, çözüm yolunun kaç adım
sürdüğünü değil. `Triatletci`'nin kendi sanal işlevleri olduğu, `Yuzucu` ve `Kosucu`'nun
da birbirinden farklı aynı imzalı işlevler tanımladığı durum — adlandırma çakışması ve
belirsiz yeniden tanımlama — bu dersin kapsamı dışında. Nitelenmiş erişim (`t.Yuzucu::can_`)
yalnız sanal olmayan kalıtımda gösterildi; sanal kalıtımda paylaşılan tek kopyaya
nitelenmiş adla erişmenin nasıl davrandığı — ki normal erişimden farksız olurdu, çünkü
kopya zaten tek — bu ders ayrıca sınamadı.

#### Özet

- Çoklu kalıtımda yalnız **ilk** taban sınıfın alt nesnesi türetilmiş nesneyle aynı
  adresi paylaşıyor; sonraki taban alt nesneleri farklı bir ofsette duruyor.
- İki taban sınıfın ortak bir atası varsa ve kalıtım sanal değilse, o ata türetilmiş
  nesnenin içinde **iki ayrı kopya** olarak yer kaplıyor — elmas sorunu.
- `sizeof`, iki ayrı kopyanın varlığını bellek boyutunda doğruluyor; iki farklı yoldan
  ulaşılan ortak ata nesnesi farklı adresler taşıyor.
- `virtual public` kalıtım, ortak atayı en tepedeki türetilmiş sınıfın paylaştığı **tek**
  bir alt nesneye indiriyor; iki yoldan ulaşılan ata artık aynı adresi taşıyor.
- Bu dersin ölçtüğü ifade **parça** sınıfında: taban alt nesne türetilmiş nesnenin bir
  parçası, ama çoklu ve elmas biçimli kalıtımda o parçanın adresi ve sayısı değişiyor.

#### Sonraki Adım

Bu derste ölçülen hep normal akışta tamamlanan kurucu ve yıkıcı çağrılarıydı; hiçbir
kurucu bir hata fırlatmadı. Bir sonraki ders, İstisnalar, bir kurucunun yarıda kesildiği
— bir hata fırlattığı — durumu ölçüyor: yapım tamamlanmadan fırlatılan bir istisna,
o nesnenin zaten kurulmuş parçalarının yıkımını nasıl etkiliyor ve bir istisna nesnesi
`throw` ile fırlatılıp `catch` ile yakalanırken bir **kopya** mı çıkarıyor. Ölçülen yine
bir ifadenin hangi nesneyle çalıştığı, ama bu kez soru kurma sürecinin ortasında kesilen
bir nesnenin durumu.

### İstisnalar

Source: https://academia.sh/tr/kurslar/cpp-temelleri/istisnalar

Fırlatılan bir nesnenin yakalanırken kaç kez kopyalandığının, yarıda kesilen bir yapımın hangi parçaları yıktığının ve geçici bir nesnenin isimli bir yerelden daha az kopyalandığının ölçümü.

Önceki dört ders normal akışta tamamlanan kurucu, yıkıcı ve sanal çağrıları ölçtü. Bu
ders akışın **yarıda kesildiği** durumu ele alıyor: bir işlev hata fırlattığında
(`throw`), fırlatılan nesne `catch` bloğuna ulaşana kadar bir ya da daha çok kez
kopyalanabiliyor; kesilme bir kurucunun ortasında olduysa, kurulmuş parçalar yıkılıyor
ama nesnenin kendisi hiç yıkılmıyor. Ölçü sorusu: bir ifade kaç kez, hangi nesnenin
kopyasıyla çalışıyor?

#### Değerle Yakalama, Referansla Yakalamadan Bir Kopya Fazla Üretiyor

`HataNesnesi`, kopya kurucusunda bir sayaç artırıyor. Aynı isimli yerel nesneyi
fırlatıp bir kez değerle, bir kez referansla yakaladığımızda, sayaç kaç kopya
gösteriyor?

```cpp
// istisna_kopya_sayaci.cpp — deger ile yakalama referansla yakalamadan bir kopya fazla uretiyor
#include <iostream>

class HataNesnesi {
public:
    HataNesnesi() = default;
    HataNesnesi(const HataNesnesi&) { ++kopya_sayaci; }
    static int kopya_sayaci;
};
int HataNesnesi::kopya_sayaci = 0;

void firlat() {
    HataNesnesi h;
    throw h;
}

int main() {
    try {
        firlat();
    } catch (HataNesnesi h) {
        (void)h;
    }
    std::cout << "deger ile yakalamadan sonra kopya sayisi " << HataNesnesi::kopya_sayaci << "\n";

    HataNesnesi::kopya_sayaci = 0;
    try {
        firlat();
    } catch (const HataNesnesi& h) {
        (void)h;
    }
    std::cout << "referansla yakalamadan sonra kopya sayisi " << HataNesnesi::kopya_sayaci << "\n";

    return 0;
}
```

```
deger ile yakalamadan sonra kopya sayisi 2
referansla yakalamadan sonra kopya sayisi 1
```

**CB13.** `throw h;` isimli bir yereli fırlatıyor — istisna depolamasına bir kopya
çıkarıyor (birinci). `catch (HataNesnesi h)` bunu **değerle** yakalayıp ikinci bir
kopya çıkarıyor: toplam **2**. `catch (const HataNesnesi& h)` depolanmış nesneye
doğrudan bağlanıyor, ek kopya çıkarmıyor: toplam **1**. Sınıf **kopya**: değerle
yakalama, referansla yakalamanın işine bir kopya daha ekliyor.

#### Yapımı Yarıda Kesen İstisna: Kurulan Parçalar Yıkılıyor, Nesnenin Kendisi Yıkılmıyor

`Bilesik`'in kurucusu iki `AltParca` üyesini kurduktan sonra bir istisna fırlatıyor.
Zaten kurulmuş üyeler yıkılıyor mu? `Bilesik`'in kendi yıkıcısı hiç çalışıyor mu?

```cpp
// istisna_yarida_kesilen_yapim.cpp — kurulan parcalar yikiliyor, nesnenin kendisi hic yikilmiyor
#include <iostream>
#include <stdexcept>
#include <string>
#include <utility>

class AltParca {
public:
    explicit AltParca(std::string ad) : ad_(std::move(ad)) {
        std::cout << ad_ << " kuruldu\n";
    }
    ~AltParca() {
        std::cout << ad_ << " yikildi\n";
    }
private:
    std::string ad_;
};

class Bilesik {
public:
    Bilesik() : birinci_("birinci"), ikinci_("ikinci") {
        std::cout << "Bilesik govdesi calisiyor, istisna firlatiliyor\n";
        throw std::runtime_error("yapim hatasi");
    }
    ~Bilesik() {
        std::cout << "Bilesik yikici calisti\n";
    }
private:
    AltParca birinci_;
    AltParca ikinci_;
};

int main() {
    try {
        Bilesik b;
    } catch (const std::exception& e) {
        std::cout << "yakalanan hata: " << e.what() << "\n";
    }
    return 0;
}
```

```
birinci kuruldu
ikinci kuruldu
Bilesik govdesi calisiyor, istisna firlatiliyor
ikinci yikildi
birinci yikildi
yakalanan hata: yapim hatasi
```

**CB14.** `birinci_` ve `ikinci_` üye ilkleme listesinde kuruluyor. Gövdede fırlatılan
istisna kurucuyu yarıda kesiyor: `Bilesik` **hiçbir zaman tamamlanmadı**. Yığıt
çözülürken o ana kadar kurulmuş üyeler ters sırayla yıkılıyor — önce `ikinci_`, sonra
`birinci_` — ama "Bilesik yikici calisti" hiç basılmadı: yıkıcı ancak kurucu
**tamamlanmışsa** çağrılıyor. Sınıf **kopya** burada doğrudan görünmüyor, ama
`catch (const std::exception& e)` referansla yakalayıp ek kopya çıkarmıyor.

#### Geçici Bir Nesne, İsimli Bir Yerelden Daha Az Kopyalanıyor

CB13'te isimli bir yerel fırlatılıp değerle yakalanınca kopya sayısı **2** çıktı.
Doğrudan bir geçici (`throw HataNesnesi();`) fırlatılıp yine değerle yakalanırsa bu
sayı değişiyor mu?

```cpp
// istisna_gecici_kopya.cpp — gecici nesne firlatmak isimli yerelden daha az kopyaliyor
#include <iostream>

class HataNesnesi {
public:
    HataNesnesi() = default;
    HataNesnesi(const HataNesnesi&) { ++kopya_sayaci; }
    static int kopya_sayaci;
};
int HataNesnesi::kopya_sayaci = 0;

void firlat_gecici() {
    throw HataNesnesi();
}

int main() {
    try {
        firlat_gecici();
    } catch (HataNesnesi h) {
        (void)h;
    }
    std::cout << "gecici nesne firlatilinca (deger ile yakalama) kopya sayisi "
              << HataNesnesi::kopya_sayaci << "\n";

    return 0;
}
```

```
gecici nesne firlatilinca (deger ile yakalama) kopya sayisi 1
```

**CB15.** `firlat_gecici` isimli bir yerel kurmuyor, doğrudan bir geçici (prvalue)
kurup fırlatıyor; C++17'de bir geçiciden (prvalue) ilklenen istisna nesnesi
kopya kurucu çağrılmadan doğrudan istisna depolamasında kurulur, birinci kopya
düşüyor. Kalan tek kopya değerle yakalamanınki: toplam **1**, CB13'teki **2**'den bir
eksik. Sınıf **kopya**: fırlatılan ifadenin isimli yerel mi geçici mi olduğu, kopya
sayısını değiştiriyor.

#### Yığıt Çözülmesi Çok Çerçeveli: Her Katman Kendi Yerellerini Yıkıyor

CB14 tek bir kurucunun içinde yarıda kalan yapımı ölçtü. İstisna birkaç iç içe işlev
çağrısının en derininde fırlatılırsa, ara çerçevelerdeki yerel nesneler de aynı
yığıt-çözülme mekanizmasıyla yıkılıyor mu, ve hangi sırayla?

```cpp
// istisna_yigit_cozulmesi.cpp — cok cerceveli yigit cozulmesinde her cerceve kendi yerellerini ters sirayla yikiyor
#include <iostream>
#include <stdexcept>
#include <string>
#include <utility>

class Izleyici {
public:
    explicit Izleyici(std::string ad) : ad_(std::move(ad)) {
        std::cout << ad_ << " kuruldu\n";
    }
    ~Izleyici() {
        std::cout << ad_ << " yikildi (yigit cozulurken)\n";
    }
private:
    std::string ad_;
};

void ucuncu_seviye() {
    Izleyici i3("ucuncu seviye");
    throw std::runtime_error("derinlerde hata");
}

void ikinci_seviye() {
    Izleyici i2("ikinci seviye");
    ucuncu_seviye();
}

void birinci_seviye() {
    Izleyici i1("birinci seviye");
    ikinci_seviye();
}

int main() {
    try {
        birinci_seviye();
    } catch (const std::exception& e) {
        std::cout << "yakalanan hata: " << e.what() << "\n";
    }
    return 0;
}
```

```
birinci seviye kuruldu
ikinci seviye kuruldu
ucuncu seviye kuruldu
ucuncu seviye yikildi (yigit cozulurken)
ikinci seviye yikildi (yigit cozulurken)
birinci seviye yikildi (yigit cozulurken)
yakalanan hata: derinlerde hata
```

**CB35.** Üç çerçevenin yerelleri kuruluş sırasıyla basılıyor, yıkım tersi sırayla:
önce `i3`, sonra `i2`, sonra `i1`. Yığıt çözülmesi `catch`'e ulaşana kadar geçtiği
**her** çerçevenin yerellerini yıkıyor — CB14'ün tek kurucudaki kuralının çağrı
yığıtının tamamına genellenmiş hâli. Sınıf **kopya** görünmüyor, ama düzenek aynı.

#### throw; Orijinal Nesneyi Koruyor, throw e; Statik Tipe Sadeliyor

Yakalanan bir istisnayı yeniden fırlatmanın iki yazımı var: parametresiz `throw;` ve
yakalanan değişkeni fırlatan `throw e;`. İkisi aynı istisnayı mı taşıyor dışarıya?

```cpp
// istisna_throw_vs_throw_e.cpp — throw; orijinal tipi koruyor, throw e; parametrenin statik tipine sadeliyor
#include <iostream>
#include <stdexcept>

class TabanHata : public std::runtime_error {
public:
    TabanHata() : std::runtime_error("taban hata") {}
};

class TuremisHata : public TabanHata {
public:
    TuremisHata() = default;
};

void yeniden_firlat_orijinal() {
    try {
        throw TuremisHata();
    } catch (TabanHata&) {
        throw; // yakalanan nesneyi oldugu gibi, dinamik tipiyle yeniden firlatiyor
    }
}

void yeniden_firlat_sadelenmis() {
    try {
        throw TuremisHata();
    } catch (TabanHata& e) {
        throw e; // e'nin statik tipinden (TabanHata) yeni bir nesne kuruyor
    }
}

int main() {
    try {
        yeniden_firlat_orijinal();
    } catch (TuremisHata&) {
        std::cout << "throw; sonrasi TuremisHata olarak yakalandi\n";
    } catch (TabanHata&) {
        std::cout << "throw; sonrasi TabanHata olarak yakalandi\n";
    }

    try {
        yeniden_firlat_sadelenmis();
    } catch (TuremisHata&) {
        std::cout << "throw e; sonrasi TuremisHata olarak yakalandi\n";
    } catch (TabanHata&) {
        std::cout << "throw e; sonrasi TabanHata olarak yakalandi\n";
    }

    return 0;
}
```

```
throw; sonrasi TuremisHata olarak yakalandi
throw e; sonrasi TabanHata olarak yakalandi
```

**CB36.** `throw;` yakalanan nesneyi olduğu gibi yeniden fırlatıyor, dış `catch` onu
hâlâ **`TuremisHata`** olarak yakalıyor — dinamik tip korunuyor. `throw e;` ise
`e`'nin **statik** tipinden (`TabanHata`) yeni bir nesne kuruyor; `catch
(TuremisHata&)` artık eşleşmiyor — CB21'deki nesne dilimlemeyle aynı kök. Sınıf **kopya**:
`throw e;`, orijinalin değil `e`'nin statik tipinden kurulmuş bir kopyayı taşıyor.

#### Taban Sınıfa Değerle Yakalama, Türetilmiş İstisnayı Diliyor

`TuremisHata`, `tip()` sanal işlevini yeniden tanımlıyor. Aynı nesne bir kez değerle
bir kez referansla `TabanHata` olarak yakalanınca, `tip()` hangi sürüme ulaşıyor?

```cpp
// istisna_deger_ile_yakalama_dilimleme.cpp — turetilmis istisna tabana deger ile yakalaninca diliniyor, referansla yakalaninca dilinmiyor
#include <iostream>
#include <stdexcept>

class TabanHata : public std::exception {
public:
    virtual const char* tip() const { return "TabanHata"; }
};

class TuremisHata : public TabanHata {
public:
    const char* tip() const override { return "TuremisHata"; }
};

void firlat_turemis() {
    throw TuremisHata();
}

int main() {
    try {
        firlat_turemis();
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcatch-value="
    } catch (TabanHata e) { // derleyici burada bilerek deger ile yakalandigini uyariyor, olcum tam da bu davranisi gosteriyor
#pragma GCC diagnostic pop
        std::cout << "deger ile yakalanan nesnenin tip() sonucu: " << e.tip() << "\n";
    }

    try {
        firlat_turemis();
    } catch (const TabanHata& e) {
        std::cout << "referansla yakalanan nesnenin tip() sonucu: " << e.tip() << "\n";
    }

    return 0;
}
```

```
deger ile yakalanan nesnenin tip() sonucu: TabanHata
referansla yakalanan nesnenin tip() sonucu: TuremisHata
```

**CB37.** `catch (TabanHata e)` değerle yakalıyor: nesne `TabanHata` tipinde yeni bir
nesneye kopyalanıyor — CB21'dekine benzer dilimleme, `tip()` **`TabanHata`**'ya
ulaşıyor. `catch (const TabanHata& e)` depolanmış `TuremisHata`'ya doğrudan
bağlanıyor, dilimlemiyor: **`TuremisHata`**. Bu yüzden istisna hiyerarşileri
neredeyse hep **referansla** yakalanır. Sınıf **kopya**: `catch (TabanHata e)`'nin
ürettiği nesne, orijinalin `TabanHata` parçasının bir kopyası.

#### Üç İstisna Güvencesi Düzeyi: Güçlü Durumu Korur, Temel Durumu Değiştirebilir

İstisna fırlatan bir işlevden sonra nesnenin durumu üç düzeyden birinde olabilir:
**güçlü güvence** (öncesi duruma dönülür), **temel güvence** (geçerli ama değişmiş
olabilir), **fırlatmama güvencesi** (sıradaki ölçüm). Aynı sınırı aşan iki ekleme
işlevi — biri güçlü biri temel güvenceyle yazılmış — başarısız çağrı sonrası nasıl
farklılaşıyor?

```cpp
// istisna_guvence_duzeyleri.cpp — guclu guvence basarisiz cagridan sonra durumu degistirmiyor, temel guvence degistirebiliyor
#include <iostream>
#include <stdexcept>

class Sayac {
public:
    void guclu_ekle(int miktar) {
        int yeni_deger = deger_ + miktar; // hesap yerel degiskende, deger_ henuz degismedi
        if (yeni_deger > 100) {
            throw std::runtime_error("sinir asildi");
        }
        deger_ = yeni_deger; // yalnizca basari kesinlesince atama yapiliyor
    }

    void temel_ekle(int miktar) {
        deger_ += miktar / 2;             // once yarisi dogrudan uygulaniyor
        if (deger_ + miktar / 2 > 100) {
            throw std::runtime_error("sinir asildi");
        }
        deger_ += miktar / 2;             // sonra kalan yari
    }

    int deger() const { return deger_; }

private:
    int deger_ = 90;
};

int main() {
    Sayac s1;
    try {
        s1.guclu_ekle(20);
    } catch (const std::exception&) {
        std::cout << std::boolalpha
                  << "guclu_ekle basarisiz oldu, deger degisti mi: "
                  << (s1.deger() != 90) << "\n";
    }

    Sayac s2;
    try {
        s2.temel_ekle(20);
    } catch (const std::exception&) {
        std::cout << std::boolalpha
                  << "temel_ekle basarisiz oldu, deger degisti mi: "
                  << (s2.deger() != 90) << "\n";
    }

    return 0;
}
```

```
guclu_ekle basarisiz oldu, deger degisti mi: false
temel_ekle basarisiz oldu, deger degisti mi: true
```

**CB38.** `guclu_ekle` sınır kontrolünü **yerel bir değişkende** yapıp `deger_`'e
yalnız başarı kesinleşince atıyor; `deger_` hiç dokunulmuyor: **false** — güçlü
güvence. `temel_ekle` sınırı **iki adımda** uygulayıp kontrolü aralarına
sıkıştırıyor; fırlatma anında `deger_` zaten kısmen değişmiş: **true** — temel
güvence. Üçüncü düzey sıradaki ölçümde `noexcept` üzerinden ele alınıyor. Sınıf
**kendisi**: fark nesnenin kimliğinde değil, çağrının onu bıraktığı durumda.

#### Kopyala, Değiştir, Değiştir-Tokuş Et: Güçlü Güvenceyi Yapıdan Kazanmak

Atama işlecini elle yazmak yerine parametreyi **değerle** alıp bir `takas` işleviyle
değiştirmek de güçlü güvence sağlayan bilinen bir kalıp. Normal atamada ve öz atamada
(`c = c;`) doğru sonucu veriyor mu?

```cpp
// istisna_kopyala_degistir_tokusla.cpp — kopyala-degistir-degistir-tokus et deseni normal atamada ve oz atamada dogru sonucu veriyor
#include <iostream>
#include <string>
#include <utility>

class Tampon {
public:
    explicit Tampon(std::string veri) : veri_(std::move(veri)) {}
    Tampon(const Tampon& diger) : veri_(diger.veri_) {}

    friend void takas(Tampon& a, Tampon& b) noexcept {
        using std::swap;
        swap(a.veri_, b.veri_);
    }

    Tampon& operator=(Tampon diger) { // deger ile aliniyor: kopya cagrinin kendisinde olusuyor
        takas(*this, diger);
        return *this;
    } // diger burada yikiliyor, eski veri_ onunla birlikte gidiyor

    const std::string& veri() const { return veri_; }

private:
    std::string veri_;
};

int main() {
    Tampon a("birinci");
    Tampon b("ikinci");
    a = b;
    std::cout << "atama sonrasi a: " << a.veri() << ", b: " << b.veri() << "\n";

    Tampon c("ucuncu");
    c = c;
    std::cout << "oz atama sonrasi c: " << c.veri() << "\n";

    return 0;
}
```

```
atama sonrasi a: ikinci, b: ikinci
oz atama sonrasi c: ucuncu
```

**CB39.** `operator=(Tampon diger)` parametreyi **değerle** alıyor; `diger` çağrı
sırasında bağımsız bir kopya olarak kuruluyor — kopyalama, gövde çalışmadan **önce**,
fırlayabilecek tek yerde tamamlanıyor. Gövde yalnız `noexcept` `takas`'ı çağırıyor: bu
sıralama güçlü güvenceyi otomatik veriyor, kopyalama başarısız olursa `*this` hiç
değişmez. Öz atamada (`c = c;`) özel bir kontrole gerek kalmıyor, `diger` bağımsız bir
kopya olduğu için sonuç yine **ucuncu**. Sınıf **kopya**: güvence, atamadan önce ayrı
bir kopya çıkarılmasına dayanıyor.

#### noexcept: Fırlatmama Sözleşmesi ve Yeniden Ayırmada Taşıma Seçimi

`std::vector` kapasitesini aştığında yeniden ayırma yapıp eski elemanları taşıyor ya
da kopyalıyor. Standart, taşıma kurucusu `noexcept` **değilse** kopyalamayı zorunlu
kılıyor — yeniden ayırma sırasında bir taşımanın ortasında fırlayan istisna elemanları
kurtarılamaz bırakabilirdi. İki sınıf, biri `noexcept` taşıma kurucusuyla biri onsuz,
`emplace_back` ile büyüdüğünde gerçekten farklı davranıyor mu?

```cpp
// istisna_noexcept_vector_secimi.cpp — noexcept isaretli tasima kurucusu vector yeniden ayirmasinda kopyanin onune geciyor
#include <iostream>
#include <vector>

class GuvenliTasima {
public:
    GuvenliTasima() = default;
    GuvenliTasima(const GuvenliTasima&) { ++kopya_sayaci; }
    GuvenliTasima(GuvenliTasima&&) noexcept { ++tasima_sayaci; }
    static int kopya_sayaci;
    static int tasima_sayaci;
};
int GuvenliTasima::kopya_sayaci = 0;
int GuvenliTasima::tasima_sayaci = 0;

class GuvensizTasima {
public:
    GuvensizTasima() = default;
    GuvensizTasima(const GuvensizTasima&) { ++kopya_sayaci; }
    GuvensizTasima(GuvensizTasima&&) { ++tasima_sayaci; } // noexcept degil
    static int kopya_sayaci;
    static int tasima_sayaci;
};
int GuvensizTasima::kopya_sayaci = 0;
int GuvensizTasima::tasima_sayaci = 0;

int main() {
    {
        std::vector<GuvenliTasima> v;
        v.reserve(1);
        for (int i = 0; i < 5; ++i) {
            v.emplace_back();
        }
    }
    std::cout << std::boolalpha
              << "noexcept tasima kurucusunda yeniden ayirma hic kopya kullandi mi: "
              << (GuvenliTasima::kopya_sayaci > 0) << "\n";
    std::cout << "noexcept tasima kurucusunda yeniden ayirma tasima kullandi mi: "
              << (GuvenliTasima::tasima_sayaci > 0) << "\n";

    {
        std::vector<GuvensizTasima> v;
        v.reserve(1);
        for (int i = 0; i < 5; ++i) {
            v.emplace_back();
        }
    }
    std::cout << "noexcept olmayan tasima kurucusunda yeniden ayirma hic kopya kullandi mi: "
              << (GuvensizTasima::kopya_sayaci > 0) << "\n";

    return 0;
}
```

```
noexcept tasima kurucusunda yeniden ayirma hic kopya kullandi mi: false
noexcept tasima kurucusunda yeniden ayirma tasima kullandi mi: true
noexcept olmayan tasima kurucusunda yeniden ayirma hic kopya kullandi mi: true
```

**CB40.** `GuvenliTasima`'nın taşıma kurucusu `noexcept`: `vector` yeniden ayırırken
kopyayı hiç kullanmıyor — `kopya_sayaci > 0` **false**, `tasima_sayaci > 0` **true**.
`GuvensizTasima`'nınki `noexcept` değil: `vector` kopyalamayı seçiyor —
`kopya_sayaci > 0` **true**. `noexcept` yalnız bir belge notu değil, standart
kütüphane bu sözleşmeye göre **farklı kod** çalıştırıyor; sözü tutmayıp fırlaması
`std::terminate`'i tetikler, o yol burada çalıştırılmadı. Sınıf **kopya**:
`noexcept`'in varlığı, elemanların kopya mı taşıma mı ile taşınacağını belirliyor.

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

Bu derste istisnalar hep tek ya da sıralı birkaç `catch` bloğuyla yakalandı;
`catch (...)` ile her tipin yakalanması ve aynı hiyerarşide **kısmi** eşleşme
kapsam dışı kaldı. `noexcept` işaretli bir işlevden gerçekten fırlatmanın
`std::terminate`'i tetiklemesi ölçülmedi — program temiz bir çıkışla sonlanamayacağı
için sandbox'ta gösterilemiyor; yalnız sözleşmenin tutulduğu durum ölçüldü.
Kopya-değiştir-tokuş kalıbının asıl güvencesi — kopya kurucusunun **kendisi**
fırlarsa `*this`'in dokunulmamış kalması — burada yalnız dolaylı olarak ele alındı,
kopya kurucusunun gerçekten fırlattığı durum ayrıca çalıştırılmadı.

#### Özet

- `throw h;` isimli bir yerel değişkeni istisna depolamasına bir kez kopyalıyor;
  `catch (HataNesnesi h)` bu depolanmış nesneyi değerle yakalayarak ikinci bir kopya
  daha çıkarıyor.
- `catch (const HataNesnesi&)` depolanmış nesneye doğrudan bağlanıyor, ek bir kopya
  çıkarmıyor.
- Bir kurucunun gövdesinde fırlatılan istisna, o ana kadar kurulmuş üyeleri kuruluş
  sırasının tersiyle yıkıyor, ama nesnenin kendi yıkıcısını hiç çağırmıyor — kurucu
  tamamlanmadığı için.
- Doğrudan bir geçici nesne fırlatmak (`throw HataNesnesi();`), isimli bir yerel
  fırlatmaktan bir kopya eksik üretiyor.
- Bu dersin ölçtüğü ifade **kopya** sınıfında: bir istisnanın fırlatılma ve yakalanma biçimi,
  sonunda kaç kopyanın çıkarıldığını doğrudan belirliyor.

#### Sonraki Adım

Bu derste `HataNesnesi::kopya_sayaci`, `static` bir üye olarak her çağrıda aynı
sayacı paylaştı. Bir sonraki ders, Ad Alanları ve Kod Düzeni, aynı ada sahip iki
sembolün ad alanlarıyla nasıl ayrıştırıldığını ve `static`/`extern` bağlantısının bir
sembolü tek dosyaya mı hapsettiğini yoksa dosyalar arasında mı paylaştırdığını
ölçüyor — yine bir ifadenin hangi sembolle çalıştığı sorusu, ama artık sınıf değil
dosya sınırları üzerinden.

### Ad Alanları ve Kod Düzeni

Source: https://academia.sh/tr/kurslar/cpp-temelleri/ad-alanlari-ve-kod-duzeni

Aynı ada sahip iki sembolün ad alanlarıyla nasıl ayrıştığının, bir using bildiriminin ve bir ad alanı takma adının yeni bir sembol değil, var olana bir erişim yolu olduğunun ölçümü.

Önceki ders `HataNesnesi::kopya_sayaci`'nin tek bir çeviri biriminde tanımlı `static`
bir üye olarak her çağrıda aynı sayacı paylaştığını gösterdi — ölçü sınıf ve nesne
sınırlarındaydı. Bu ders sınırı bir üst düzeye taşıyor: **ad alanları**. İki farklı
kütüphane ya da modül aynı ismi (`hesapla`, `Arac`) kullanmak isteyebilir; ad alanları
bu çakışmayı, her sembolü kendi ad alanının içine yerleştirerek çözüyor. Bu, kursun
başından beri ölçülen "hangi nesneyle çalışıyor" sorusundan farklı bir eksen: burada
nesne değil, **sembolün kendisi** — bir isim hangi tanıma karşılık geliyor — ölçülüyor.
Ölçü sorusu yine benzer bir kalıp izliyor — bir ifade hangi sembolle çalışıyor — ama
artık sınıf ya da nesne değil, bir isim ve onu barındıran ad alanı ölçülüyor. Kod
düzeni açısından bunun karşılığı basit: bir kütüphane, birbiriyle ilişkili adları tek
bir ad alanının altına toplayarak hem çakışmayı önlüyor hem de o adların nereden
geldiğini okuyan için belirginleştiriyor.

#### Aynı Ada Sahip İki Farklı Sembol: Ad Alanları Ayırıyor

`kara` ve `deniz` ad alanlarının ikisi de `hesapla` adında bir işlev tanımlıyor. Nitelikli
adla (`kara::hesapla`, `deniz::hesapla`) çağrıldıklarında, bunlar gerçekten iki farklı
işlev mi, yoksa bir şekilde aynı sembole mi çözülüyor?

```cpp
// ad_alani_ayni_isim.cpp — ad alanlari ayni isimli iki farkli sembolu ayristiriyor
#include <iostream>

namespace kara {
int hesapla(int taban) {
    return taban * 2;
}
}

namespace deniz {
int hesapla(int taban) {
    return taban * 3;
}
}

int main() {
    std::cout << "kara::hesapla(5) sonucu " << kara::hesapla(5) << "\n";
    std::cout << "deniz::hesapla(5) sonucu " << deniz::hesapla(5) << "\n";

    int (*kara_ptr)(int) = kara::hesapla;
    int (*deniz_ptr)(int) = deniz::hesapla;

    std::cout << std::boolalpha
              << "kara::hesapla ile deniz::hesapla ayni islev mu "
              << (kara_ptr == deniz_ptr) << "\n";

    return 0;
}
```

```
kara::hesapla(5) sonucu 10
deniz::hesapla(5) sonucu 15
kara::hesapla ile deniz::hesapla ayni islev mu false
```

**CB16.** `kara::hesapla(5)` ve `deniz::hesapla(5)` farklı sonuçlar veriyor (10 ve 15) —
aynı isim, iki bağımsız gövde. `kara_ptr == deniz_ptr` karşılaştırması bunu işlev
işaretçisi düzeyinde doğruluyor: **false**, çünkü ikisi gerçekten farklı iki sembol.
Ad alanı olmasaydı, aynı imzalı iki `hesapla` aynı çeviri biriminde tanımlanamaz, bir
yeniden tanımlama hatası verirdi. (M11/K03'ün Kütüphane Üretimi dersinde ölçülen
`static`/`extern` bağlantı, bir sembolün bir dosyaya mı hapsolduğunu yoksa dosyalar
arasında mı paylaşıldığını belirliyordu; burada ad alanı aynı bağlantıya sahip iki
sembolü **adlarıyla** ayrıştırıyor — aynı sorunun farklı bir mekanizmadan çözümü.)
Sınıf **kendisi**: nitelikli bir ad (`kara::hesapla`) her zaman kendi ad alanındaki
tikel sembole ulaşıyor, başka bir ad alanındaki eş adlı sembole hiç karışmadan.

#### `using` Bildirimi Yeni Bir Sembol Kurmuyor, Var Olana Erişim Açıyor

`kullan` işlevi içinde `using kara::hesapla;` yazıldığında, ortaya çıkan yerel `hesapla`
adı yeni bir sembol mü, yoksa `kara::hesapla`'ya açılan bir erişim yolu mu?

```cpp
// ad_alani_using.cpp — using bildirimi yeni sembol degil, var olana erisim yolu aciyor
#include <iostream>

namespace kara {
int hesapla(int taban) {
    return taban * 2;
}
}

void kullan() {
    using kara::hesapla;
    int (*yerel_ptr)(int) = hesapla;
    int (*dogrudan_ptr)(int) = kara::hesapla;

    std::cout << std::boolalpha
              << "using ile erisilen hesapla dogrudan kara::hesapla ile ayni mi "
              << (yerel_ptr == dogrudan_ptr) << "\n";
}

int main() {
    kullan();
    return 0;
}
```

```
using ile erisilen hesapla dogrudan kara::hesapla ile ayni mi true
```

**CB17.** `using kara::hesapla;` `kullan` işlevi içinde `hesapla` adını nitelemeden
kullanmayı sağlıyor, ama işaret ettiği şey değişmiyor — `yerel_ptr` (nitelemeden
alınan) ile `dogrudan_ptr` (`kara::hesapla` ile doğrudan alınan) aynı işlev
işaretçisini taşıyor, **true**. `using` bildirimi bir kopya çıkarmıyor, yeni bir işlev
de kurmuyor; yalnız derleyiciye "bu nitelenmemiş ad, şu nitelenmiş sembole karşılık
gelsin" diyor. Sınıf **kendisi**: `using` sonrası nitelenmemiş çağrı, nitelenmiş
çağrıyla tam olarak aynı sembole ulaşıyor.

#### İç İçe Bir Ad Alanına İki Farklı Erişim Yolu

`sirket::filo` iç içe bir ad alanı. `arac_sayisi`'ne bir işlev tam nitelenmiş adla, bir
başka işlev bir ad alanı takma adıyla (`namespace f = sirket::filo;`) erişiyor. İkisi
de aynı değişkene mi ulaşıyor?

```cpp
// ad_alani_ic_ice.cpp — tam nitelenmis ad ve takma ad ayni degiskene ulasiyor
#include <iostream>

namespace sirket::filo {
int arac_sayisi = 12;
}

void* birinci_erisim() {
    return static_cast<void*>(&sirket::filo::arac_sayisi);
}

void* ikinci_erisim() {
    namespace f = sirket::filo;
    return static_cast<void*>(&f::arac_sayisi);
}

int main() {
    std::cout << std::boolalpha
              << "tam nitelenmis adla ve takma adla erisilen degiskenin ayni olup olmadigi "
              << (birinci_erisim() == ikinci_erisim()) << "\n";
    return 0;
}
```

```
tam nitelenmis adla ve takma adla erisilen degiskenin ayni olup olmadigi true
```

**CB18.** `namespace sirket::filo { ... }` C++17'nin iç içe ad alanı sözdizimiyle tek
satırda iki katman açıyor. `birinci_erisim` `arac_sayisi`'ne tam nitelenmiş adla
ulaşıyor; `ikinci_erisim` önce `f`'yi `sirket::filo`'nun bir **takma adı** olarak
tanımlayıp `f::arac_sayisi` üzerinden ulaşıyor. İki erişim yolunun döndürdüğü adresler
karşılaştırıldığında **true**: takma ad da, tıpkı `using` bildirimi gibi, yeni bir
değişken kurmuyor, var olan tek `arac_sayisi`'ne kısaltılmış bir yol açıyor. Sınıf
**kendisi**: erişim yolu ne kadar farklı görünürse görünsün (tam ad, takma ad,
`using`), hepsi aynı tikel sembole ulaşıyor.

#### Adsız Ad Alanı: Sembolü Kendi Birimine Hapsetmek

İki farklı çeviri birimi, her biri kendi adsız (anonim) ad alanı içinde, aynı isimli
(`deger`) bir işlev tanımlıyor. İki birim ayrı ayrı derlenip bağlandığında bu isim
çakışması bir bağlama hatası mı çıkarıyor, yoksa her birim kendi tanımını mı koruyor?

```cpp
// ad_alani_ic_baglanti_birim_a.cpp — adsiz ad alani icindeki sembol bu birime ozgu, disaridan gorunmuyor
namespace {
int deger() { return 111; }
}

int birim_a_deger() {
    return deger();
}
```

```cpp
// ad_alani_ic_baglanti_birim_b.cpp — ayni isimli baska bir adsiz ad alani sembolu, farkli birimde, cakismiyor
namespace {
int deger() { return 222; }
}

int birim_b_deger() {
    return deger();
}
```

```cpp
// ad_alani_ic_baglanti_ana.cpp — iki farkli birimdeki ayni isimli adsiz ad alani sembolu cakismadan derlenip baglaniyor
#include <iostream>

int birim_a_deger();
int birim_b_deger();

int main() {
    std::cout << "birim_a_deger() sonucu " << birim_a_deger() << "\n";
    std::cout << "birim_b_deger() sonucu " << birim_b_deger() << "\n";
    return 0;
}
```

```
birim_a_deger() sonucu 111
birim_b_deger() sonucu 222
```

**CB41.** İki birim de kendi `deger()`'ini çağırıyor ve her biri kendi değerini
döndürüyor — **111** ve **222**, birbirine hiç karışmadan. Adsız bir ad alanı içindeki
her sembol, derleyici tarafından o çeviri birimine özgü, dışarıdan görünmeyen bir isme
dönüştürülüyor; bu yüzden iki ayrı `.cpp` dosyası aynı adı taşıyan bir `deger()`
tanımlasa bile bağlayıcı bunu bir çakışma olarak görmüyor. Bu, `M11/K03`'ün ölçtüğü
`static` bağlantıyla aynı sonucu veriyor — bir sembolü bir dosyaya hapsetmek — ama
adsız ad alanı bunu yalnız işlevler için değil, sınıflar ve değişkenler için de aynı
sözdizimiyle sağlıyor. Sınıf **kendisi**: `birim_a_deger()` içindeki `deger` adı,
yalnız `birim_a`'nın kendi tanımına ulaşıyor; `birim_b`'nin `deger`'iyle hiç
karışmıyor.

#### using Bildirimi Tek Sembolü Açıyor, using Yönergesi Tüm Ad Alanını

CB17 `using kara::hesapla;` bir **bildirimdi** — tek bir sembolü nitelemeden erişilebilir
kıldı. `using namespace kara;` bir **yönerge** — bütün ad alanını açar. Aynı ad
alanının iki işlevinden (`hesapla`, `ikile`) yalnız biri bildirimle içeri alınırsa,
diğerine hâlâ nitelenmiş adla mı ulaşmak gerekiyor; yönergeyle ikisine de nitelemesiz
mi ulaşılıyor?

```cpp
// ad_alani_bildirim_vs_yonerge.cpp — using bildirimi tek sembolu aciyor, using yonergesi ad alaninin tamamini aciyor
#include <iostream>

namespace kara {
int hesapla(int taban) { return taban * 2; }
int ikile(int taban) { return taban * 2; }
}

void sadece_bildirim() {
    using kara::hesapla; // yalniz hesapla nitelemesiz erisilebilir hale geliyor
    std::cout << "sadece_bildirim: hesapla(5) = " << hesapla(5) << "\n";
    std::cout << "sadece_bildirim: kara::ikile(5) = " << kara::ikile(5) << "\n";
}

void tum_ad_alani() {
    using namespace kara; // kara'nin tum sembolleri nitelemesiz erisilebilir hale geliyor
    std::cout << "tum_ad_alani: hesapla(5) = " << hesapla(5) << "\n";
    std::cout << "tum_ad_alani: ikile(5) = " << ikile(5) << "\n";
}

int main() {
    sadece_bildirim();
    tum_ad_alani();
    return 0;
}
```

```
sadece_bildirim: hesapla(5) = 10
sadece_bildirim: kara::ikile(5) = 10
tum_ad_alani: hesapla(5) = 10
tum_ad_alani: ikile(5) = 10
```

**CB42.** `sadece_bildirim` içinde `hesapla(5)` nitelemesiz çağrılabiliyor, ama
`ikile` hâlâ `kara::ikile(5)` diye nitelenmek **zorunda** — bildirim yalnız adını
verdiğiniz sembolü açıyor, `kara`'nın geri kalanına dokunmuyor. `tum_ad_alani` içinde
ise `using namespace kara;` sonrası hem `hesapla(5)` hem `ikile(5)` nitelemesiz
çağrılabiliyor — yönerge, ad alanının **tamamını** arama kapsamına ekliyor. İkisi de
aynı iki işleve ulaşıyor (her ikisi de 10 döndürüyor), fark sonuçta değil, hangi
adların nitelemesiz kullanılabilir hale geldiğinde. Sınıf **kendisi**: bildirim ve
yönerge farklı **kapsam genişliğiyle** aynı sembollere erişim açıyor, sembollerin
kendisini değiştirmiyor.

#### Çok Birimli Başlık/Kaynak Ayrımı: inline Aynı Tanımın Tekrarına İzin Veriyor

Bir başlık dosyasına yazılıp birden çok `.cpp` dosyasına dahil edilen bir işlev, o
kadar dosya sayısınca **tekrar tekrar** tanımlanmış olur — normal bir işlev bunu tek
tanım kuralına (ODR) aykırı bulur. `inline` anahtar sözcüğü bu tekrarı gerçekten
meşrulaştırıyor mu?

```cpp
// ad_alani_inline_baslik_birim1.cpp — baslik dosyasindan geldigi varsayilan inline islev, bu birimde tanimlaniyor
inline int cift(int x) {
    return x * 2;
}

int birim1_cift(int x) {
    return cift(x);
}
```

```cpp
// ad_alani_inline_baslik_birim2.cpp — ayni inline islevin token'i token'ina ozdes tanimi, baska bir birimde tekrarlaniyor
inline int cift(int x) {
    return x * 2;
}

int birim2_cift(int x) {
    return cift(x);
}
```

```cpp
// ad_alani_inline_baslik_ana.cpp — iki farkli birimdeki ozdes inline tanim, tek tanim kuralini ihlal etmeden derlenip baglaniyor
#include <iostream>

int birim1_cift(int x);
int birim2_cift(int x);

int main() {
    std::cout << "birim1_cift(21) sonucu " << birim1_cift(21) << "\n";
    std::cout << "birim2_cift(21) sonucu " << birim2_cift(21) << "\n";
    return 0;
}
```

```
birim1_cift(21) sonucu 42
birim2_cift(21) sonucu 42
```

**CB43.** İki ayrı çeviri birimi, `cift`'i **birbirinden bağımsız olarak** ama
**token'i token'ına özdeş** biçimde tanımlıyor — tıpkı bir başlık dosyasının iki
`.cpp`'ye dahil edilmesini simüle ediyor. Program başarıyla derlenip bağlanıyor ve her
iki birim de aynı sonucu üretiyor: **42**. `inline`, tek tanım kuralına bir istisna
açıyor: bir işlev birden çok çeviri biriminde tanımlanabilir, **yeter ki her tanım
birebir aynı olsun**. Bu, adsız ad alanının (CB41) çözümünün tam tersi bir strateji —
adsız ad alanı sembolü **gizleyerek** çakışmayı önlüyordu, `inline` ise sembolü
**paylaşarak** ve tanımların aynılığını bir sözleşme hâline getirerek önlüyor. İki
tanım birbirinden farklı olsaydı (örneğin biri `x * 2`, diğeri `x * 3` döndürseydi),
program standarda göre biçimsiz (ill-formed) olurdu ve derleyicinin bunu bildirmesi
gerekmezdi (no diagnostic required) — derleyici bunu her zaman yakalayamaz, bu yüzden burada
hiç çalıştırılmadı; yalnız kuralına uyan, aynı tanımın tekrarlandığı durum ölçüldü.
Sınıf **kendisi**: `cift`, iki birimde de aynı sembole karşılık geliyor, birden fazla
kopya değil, tek bir sözleşmenin iki yerde tutulan aynı ifadesi.

#### static ile Paylaşılan (extern) Sembol Arasındaki Karşıtlık

CB41 adsız ad alanıyla iç bağlantı sağladı. `static` anahtar sözcüğü dosya kapsamında
aynı etkiyi doğrudan veriyor. Bir işlevi `static` yapmak onu gerçekten dosyaya
hapsediyor mu; buna karşılık hiç `static` ya da adsız ad alanı kullanılmadan tanımlanan
sıradan bir işlev (varsayılan bağlantısı `extern`) başka bir birimden gerçekten
çağrılabiliyor mu?

```cpp
// ad_alani_baglanti_birim1.cpp — static ic baglantili sembol bu birime ozgu, extern (varsayilan) sembol disari acik
static int ic_deger() {
    return 7;
}

int disa_acik_deger() {
    return 700;
}

int birim1_ic_deger_kullan() {
    return ic_deger();
}
```

```cpp
// ad_alani_baglanti_birim2.cpp — ayni isimli baska bir static sembol, farkli birimde, cakismadan var oluyor
static int ic_deger() {
    return 9;
}

int birim2_ic_deger_kullan() {
    return ic_deger();
}
```

```cpp
// ad_alani_baglanti_ana.cpp — static sembol her birime hapsoluyor, extern (varsayilan) sembol tum birimlerden dogrudan cagrilabiliyor
#include <iostream>

int birim1_ic_deger_kullan();
int birim2_ic_deger_kullan();
int disa_acik_deger();

int main() {
    std::cout << "birim1_ic_deger_kullan() sonucu " << birim1_ic_deger_kullan() << "\n";
    std::cout << "birim2_ic_deger_kullan() sonucu " << birim2_ic_deger_kullan() << "\n";
    std::cout << "ana birimden dogrudan cagrilan disa_acik_deger() sonucu " << disa_acik_deger() << "\n";
    return 0;
}
```

```
birim1_ic_deger_kullan() sonucu 7
birim2_ic_deger_kullan() sonucu 9
ana birimden dogrudan cagrilan disa_acik_deger() sonucu 700
```

**CB44.** `birim1` ve `birim2`'nin kendi `static ic_deger()`'leri, CB41'deki adsız ad
alanı örneğiyle birebir aynı sonucu veriyor: **7** ve **9**, birbirine karışmadan —
`static`, dosya kapsamındaki bir işlevi de tıpkı adsız ad alanı gibi iç bağlantılı
kılıyor. Ama `disa_acik_deger()` hiç `static` ya da adsız ad alanı içinde değil;
varsayılan bağlantısı **extern**, ve bu yüzden yalnız bildirilerek (`int
disa_acik_deger();`) ana birimden **doğrudan** çağrılabiliyor — **700**, kendi
biriminde tanımlanan tek nesneye ulaşıyor. Üç mekanizmanın üçü de bu derste ölçüldü:
adsız ad alanı (CB41) ve `static` (burada) sembolü dosyaya hapsediyor, `inline`
(CB43) sembolü paylaşıp tanımların aynı kalmasını zorunlu kılıyor, sıradan `extern`
bağlantı ise sembolü tek bir tanımda toplayıp her yerden erişilebilir kılıyor. Sınıf
**kendisi**: her üç mekanizma da aynı isim çakışması sorununu, sembolün **görünürlük
kapsamını** değiştirerek çözüyor; sembolün kendisi hep tikel kalıyor.

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

Bu derste ölçülen çok birimli örnekler, gerçek bir başlık dosyasını (`.h`) birden
çok `.cpp`'ye `#include` etmek yerine, aynı tanımı elle iki kaynak birimine
kopyalayarak simüle etti — önişlemcinin `#include` sırasında yaptığı metinsel
kopyalamanın kendisi bu derste ayrıca ölçülmedi. Argümana bağlı arama (ADL, `std::cout
<<` gibi bir işleci niteleme yazmadan bulan mekanizma) bu dersin kapsamı dışında;
burada her çağrı ya nitelenmiş adla ya da açık bir `using` ile yapıldı. Tek tanım
kuralının **ihlal edildiği** durum — iki birimde birbirinden farklı, `inline`
olmayan iki tanımın aynı dışa açık isimle çakışması — bağlama hatası ya da tanımsız
davranışla sonuçlanacağı için hiç çalıştırılmadı; yalnız kurala **uyan** durumlar
ölçüldü. Bir ad alanının kendisinin (işlevlerinden bağımsız olarak) birden çok
dosyada `namespace kara { ... }` bloklarıyla genişletilmesi de ayrıca gösterilmedi;
burada ad alanları hep tek dosya içinde tanımlı kaldı, yalnız onların **içindeki**
sembollerin bağlantı türü çok birimli test edildi.

#### Özet

- İki farklı ad alanındaki aynı isimli işlevler bağımsız sembollerdir; nitelikli adla
  (`kara::hesapla`) her zaman kendi tanımına ulaşılıyor.
- İşlev işaretçisi karşılaştırması bunu doğruluyor: `kara::hesapla` ile `deniz::hesapla`
  aynı işlev değil.
- Bir `using` bildirimi yeni bir sembol kurmuyor; nitelenmemiş adı var olan nitelikli
  sembole bağlıyor — sınıf **kendisi**.
- Bir ad alanı takma adı (`namespace f = sirket::filo;`) da aynı ilkeyle çalışıyor:
  yeni bir değişken kurmuyor, var olana kısaltılmış bir erişim yolu açıyor.
- Tam nitelenmiş ad, `using` ve takma ad — üçü de aynı tikel sembole ulaşıyor; farklı
  olan yalnız yazım biçimi.

#### Sonraki Adım

Bu ders boyunca `static_cast<void*>(...)` sıkça kullanıldı, ama `static_cast`'in ne
garanti ettiği ya da bir sınıfın kendi dönüşüm kurallarını nasıl tanımlayabileceği hiç
açılmadı — yalnız adres karşılaştırmak için bir araç olarak kullanıldı. Kursun kapanış
dersi, Tip Dönüşüm İşleçleri, bunu doğrudan ele alıyor: bir sınıfın kendi tipinden başka
bir tipe nasıl örtük ya da açık bir dönüşüm sunabileceğini, ve böyle bir dönüşüm
çağrıldığında ortaya çıkan değerin özgün nesneyle mi, yoksa ondan türetilmiş yeni bir
nesneyle mi çalıştığını ölçüyor.

### Tip Dönüşüm İşleçleri

Source: https://academia.sh/tr/kurslar/cpp-temelleri/tip-donusum-islecleri

static_cast, dynamic_cast, const_cast ve reinterpret_cast'ın hedef tipine göre bir referansa parça olarak mı yoksa yeni bir nesneye kopya olarak mı eriştiğinin ölçümü.

Önceki ders `using` bildiriminin ve ad alanı takma adının yeni bir sembol kurmadığını,
var olan sembole yerel bir erişim yolu açtığını gösterdi — ölçü ad alanı sınırındaydı.
Bu ders sınırı bir düzey daha indiriyor: **tip dönüşüm işleçleri**. Bir `Termometre`
nesnesini `static_cast` ile tabanı `Olcer`'e dönüştürdüğümüzde soru aynı kalıyor —
`t` hangi nesneyi anıyordu, dönüşüm hangi nesneyi veriyor? Cevap işlecin adına değil,
**hedef tipe** bağlı: aynı `static_cast`, hedef bir referans olduğunda `t`'nin içindeki
alt nesneyi anıyor; hedef bir değer olduğunda yepyeni bir nesne kuruyor.

#### Tabana Dönüştürülen Referans

`Termometre`, `Olcer`'den türüyor. `t`'yi `const Olcer&`'e `static_cast` ile
dönüştürdüğümüzde derleyici yeni bir nesne kurmuyor; `t`'nin içindeki `Olcer`
alt nesnesine daha dar bir tipten bakan bir referans üretiyor. Bu referansın
adresi `t`'nin adresiyle aynı aralıkta kalıyor — çünkü temel sınıf alt nesnesi
türetilmiş nesnenin bir parçası.

```cpp
// taban-referans.cpp — static_cast ile referans donusumu yeni nesne kurar mi
#include <cstdio>

class Olcer {
public:
    virtual ~Olcer() = default;
    void oku() const { std::printf("olcer oku cagrildi\n"); }
};

class Termometre : public Olcer {
public:
    int derece() const { return derece_; }
private:
    int derece_ = 20;
};

int main() {
    Termometre t;
    const Olcer& taban = static_cast<const Olcer&>(t);

    taban.oku();
    std::printf("ayni adres mi %s\n",
        (static_cast<const void*>(&taban) == static_cast<const void*>(&t)) ? "evet" : "hayir");
    return 0;
}
```

```text
olcer oku cagrildi
ayni adres mi evet
```

**CB45.** `taban`, `t` içindeki `Olcer` alt nesnesini anıyor; `oku()` çağrısı yeni
bir nesne üzerinde değil, `t`'nin kendi parçası üzerinde çalışıyor. `&taban` ile
`&t`'nin aynı adres aralığında çıkması bunu doğruluyor — dönüşüm bir kurucu
çağırmadı, yalnız `t`'ye bakan tipi daralttı. Bu ifadenin sınıfı **parça**.

#### Değerle Dönüşüm: Aynı İşleç, Farklı Sınıf

Aynı `t`'yi bu kez hedefi bir referans değil bir **değer** olacak şekilde
`static_cast<Olcer>(t)` ile dönüştürelim. İşleç adı aynı — `static_cast` — ama
hedef tip artık bir referans değil, `Olcer`'in kendisi. Derleyici burada `t`'nin
`Olcer` alt nesnesini kopyalayarak yepyeni, bağımsız bir `Olcer` nesnesi kuruyor.

```cpp
// deger-donusumu.cpp — static_cast deger hedefte yeni nesne kurar mi
#include <cstdio>

class Olcer {
public:
    virtual ~Olcer() = default;
    void oku() const { std::printf("olcer oku cagrildi\n"); }
};

class Termometre : public Olcer {
public:
    int derece() const { return derece_; }
private:
    int derece_ = 20;
};

int main() {
    Termometre t;
    Olcer kopya = static_cast<Olcer>(t);

    kopya.oku();
    std::printf("ayni adres mi %s\n",
        (static_cast<const void*>(&kopya) == static_cast<const void*>(&t)) ? "evet" : "hayir");
    return 0;
}
```

```text
olcer oku cagrildi
ayni adres mi hayir
```

**CB46.** `kopya`, `t`'den bağımsız yeni bir `Olcer` nesnesi; `&kopya` ile `&t`
farklı adreslerde çıkıyor. Bu, önceki bölümdeki `static_cast<const Olcer&>(t)`
ile aynı işleç olmasına rağmen tamamen farklı bir sonuç: burada `t`'nin
`Termometre`'ye özgü kısmı (`derece_`) atılıyor, yalnız `Olcer` kısmı kopyalanıyor
— bu, **nesne dilimleme**. Bu ifadenin sınıfı **kopya**.

#### Dinamik Dönüşüm: Gerçek Tipe Göre Karar

`static_cast` hedef tipe körü körüne güvenir; derleme anında karar verir.
`dynamic_cast` ise çalışma anında nesnenin gerçek tipine bakar — taban
referansının ardındaki nesne gerçekten istenen türetilmiş tipte değilse
dönüşüm başarısız olur. İşaretçi hedefinde başarısızlık `nullptr` döner;
referans hedefinde bir istisna fırlatır.

```cpp
// dinamik-donusum.cpp — dynamic_cast basarisiz asagi donusumde ne verir
#include <cstdio>
#include <typeinfo>

class Olcer {
public:
    virtual ~Olcer() = default;
};

class Termometre : public Olcer {
public:
    int derece() const { return derece_; }
private:
    int derece_ = 20;
};

class Barometre : public Olcer {
public:
    int basinc() const { return basinc_; }
private:
    int basinc_ = 1013;
};

int main() {
    Termometre t;
    Olcer& taban = t;

    Barometre* p = dynamic_cast<Barometre*>(&taban);
    std::printf("isaretci donusumu basarili mi %s\n", p ? "evet" : "hayir");

    try {
        Barometre& b = dynamic_cast<Barometre&>(taban);
        (void)b;
        std::printf("referans donusumu yakalandi mi hayir\n");
    } catch (const std::bad_cast&) {
        std::printf("referans donusumu yakalandi mi evet\n");
    }
    return 0;
}
```

```text
isaretci donusumu basarili mi hayir
referans donusumu yakalandi mi evet
```

**CB47.** `taban`'ın ardındaki gerçek nesne bir `Termometre`; `Barometre`
değil. `dynamic_cast<Barometre*>` bunu çalışma anında fark edip `nullptr`
döndürüyor — bir istisna fırlatmıyor, sadece "burada yok" diyor. Aynı
başarısızlık referans hedefinde `std::bad_cast` fırlatıyor; burada yalnız
yakalandığı yazdırılıyor, `what()`'in metni değil — mesajın kesin biçimi
kütüphane uygulamasına bağlı, dersin ölçtüğü şey değil. `p` `nullptr` olduğu
için burada üretilmiş bir nesne yok; başarısız bir `dynamic_cast` ne kopya
ne de parça verir, elde hiçbir şey kalmaz.

#### Sabitliği Kaldırmak: const_cast

`const_cast` yalnız bir referansın ya da işaretçinin sabitlik niteliğini
değiştirir; hedef tip aynı sınıf kalır, yeni bir nesne kurulmaz. Aşağıdaki
`gozden_gecir`, kendisine `const` bir referansla gelen nesneyi işaretlemek
için sabitliği bir kez kaldırıyor:

```cpp
// const-cast.cpp — sabitligi kaldirmak yeni nesne kurar mi
#include <cstdio>

class Olcer {
public:
    void isaretle() { isaretlendi_ = true; }
    bool isaretlendi() const { return isaretlendi_; }
private:
    bool isaretlendi_ = false;
};

static void gozden_gecir(const Olcer& o) {
    const_cast<Olcer&>(o).isaretle();
}

int main() {
    Olcer o;
    gozden_gecir(o);
    std::printf("isaretlendi mi %s\n", o.isaretlendi() ? "evet" : "hayir");
    return 0;
}
```

```text
isaretlendi mi evet
```

**CB48.** `const_cast<Olcer&>(o)`, `o`'nun kendisine bakan, yalnız sabitlik
niteliği kaldırılmış bir referans veriyor — ne kopya ne parça, `o`'nun
**kendisi**. Bunun güvenli olmasının koşulu `o`'nun asıl nesnesinin gerçekten
`const` olarak tanımlanmamış olması; `main`'deki `o` sabit değil, bu yüzden
`isaretle()` çağrısı tanımsız davranışa girmiyor. Asıl `const` bir nesne
üzerinde aynı çağrı tanımsız davranış olurdu — bu ders böyle bir çağrıyı
yazmıyor da çalıştırmıyor da.

#### Bit Örüntüsünü Yeniden Yorumlamak: reinterpret_cast

`reinterpret_cast` en gevşek dönüşüm: hedef tipin kaynak tiple hiçbir ilişkisi
aranmaz, yalnız aynı bit örüntüsü farklı bir tipten okunur. Garanti edilen tek
şey, aynı tipe geri dönüşün orijinal değere eşit çıkması — ara değerin kendisi
taşınabilir bir anlam taşımaz, bu yüzden bu ders onu yazdırmıyor, yalnız
gidiş-dönüş eşitliğini ölçüyor.

```cpp
// yeniden-yorumlama.cpp — reinterpret_cast gidis-donus esitligi
#include <cstdio>
#include <cstdint>

int main() {
    int deger = 42;
    int* p = &deger;

    std::uintptr_t sayisal = reinterpret_cast<std::uintptr_t>(p);
    int* geri = reinterpret_cast<int*>(sayisal);

    std::printf("gidis donus esit mi %s\n", (geri == p) ? "evet" : "hayir");
    return 0;
}
```

```text
gidis donus esit mi evet
```

**CB49.** `reinterpret_cast` de yeni bir nesne kurmuyor; `p`'nin taşıdığı bit
örüntüsünü önce bir tamsayı, sonra tekrar işaretçi olarak okuyor. `geri == p`
eşitliği aynı adresin geri geldiğini gösteriyor — ama bu garanti yalnızca
aynı tipe dönen gidiş-dönüş için geçerli; ara `sayisal` değerinin taşıdığı
sayının kendisi platforma bağlı ve dersin konusu değil.

Peki bir C tarzı dönüşüm — `(Olcer)t`, `(Olcer&)t` gibi — bu dört işleçten
hangisine çözülüyor? Derleyici sırayla dener: önce `const_cast`, sonra
`static_cast`, sonra `const_cast` ile birleşmiş `static_cast`, en sonunda
`reinterpret_cast`. İlk anlamlı olan seçiliyor — bu yüzden bir C tarzı
dönüşümün hangi işlece karşılık geldiği, hedef tip ve kaynak tip arasındaki
ilişkiye bakılarak çıkarılabilir, ama okurken bunu görmek `static_cast` ya da
`dynamic_cast` yazmaktan daha zor. Bu ders C tarzı dönüşümü kullanmıyor; adını
yalnız hangi işlece çözüldüğünü göstermek için anıyor.

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

Bu ders dört dönüşüm işlecinin dördünün de **adının** sınıfı belirlemediğini
gösterdi — belirleyen **hedef tip**. Aynı `static_cast`, hedef bir referans
olduğunda `t`'nin içindeki alt nesneyi anıyor (parça), hedef bir değer
olduğunda yeni bir nesne kuruyor (kopya). Bu ders bu ikisini ayrı ayrı ölçtü;
işlecin adına bakıp sınıfı tahmin etmek bu derste hiç yapılmadı.

Hatalı bir `static_cast` ile gerçekte o tipte olmayan bir nesneyi aşağı
dönüştürmek — örneğin `taban`'ın ardındaki nesne bir `Termometre` iken onu
`static_cast<Barometre&>` ile dönüştürmek — tanımsız davranıştır: derleyici
bunu engellemez, çalışma anında da bir denetim yapılmaz. Bu ders böyle bir
çağrıyı yazmadı da çalıştırmadı da; güvenli aşağı dönüşüm için bu dersin
gösterdiği yol `dynamic_cast`'tır. `reinterpret_cast`'ın ürettiği ara
değerlerin platforma bağlı yorumu, `const_cast`'ın gerçekten `const` bir
nesne üzerindeki tanımsız davranışı ve C tarzı dönüşümün çözüm sırasının
derleyiciler arası ayrıntıları da bu dersin dışında kaldı.

#### Özet

- `static_cast` ile bir taban referansına dönüşüm yeni bir nesne kurmuyor;
  `t`'nin içindeki alt nesneyi daha dar bir tipten anıyor.
- Aynı `static_cast`, hedef bir değer olduğunda `t`'yi dilimleyerek bağımsız
  yeni bir nesne kuruyor — işleç aynı, sınıf farklı.
- `dynamic_cast` başarısız aşağı dönüşümde işaretçi için `nullptr`, referans
  için `std::bad_cast` veriyor; hiçbir durumda yeni bir nesne kurmuyor.
- `const_cast` ve `reinterpret_cast` da yeni nesne kurmuyor, var olan nesneye
  farklı bir erişim yolu açıyor.
- Bu dersin ölçtüğü ifade — `static_cast<const Olcer&>(t)` ile elde edilen
  taban referansı — **parça** sınıfındadır: `t`'nin kendi adresini, dar bir
  tipten anıyor.

#### Kurs Kapanışı

Bu ders, **C++ Temelleri ve Nesne Tasarımı** kursunun son dersiydi. Kurs
boyunca her ders aynı soruyu farklı bir sınırda sordu: bir ifade hangi
nesneyle çalışıyor — kendisiyle mi, bir kopyasıyla mı, bir parçasıyla mı,
yoksa adı olmayan bir geçiciyle mi?

| Ders | Ölçülen ifade | Hangi nesne | Sınırlayıcı ölçüm |
| --- | --- | --- | --- |
| C ve C++ Farkları | `degerle aktarilan yapi` | kopya | Çağrı noktası sınıfı söylemez; karar parametre tipinde |
| Sınıflar ve Erişim Denetimi | `uye islevin this'i` | kendisi | `private` nesneyi değil sınıfın sınırını korur |
| Yapıcılar ve Yıkıcılar | `int'ten kurulan arguman` | geçici | `const` referansa bağlanan geçicinin ömrü uzar |
| RAII | `koruyucunun biraktigi uye` | parça | Kaynağa referans tutan koruyucunun sarkması yalnız anlatıldı |
| Kopyalama ve Atama | `derin kopyalanan tampon` | kopya | Kopyanın derinliği yalnız kopyalayan yapıcının gövdesinden okunur |
| Referanslar | `referansla aktarilan yapi` | kendisi | `const` referans parametre geçiciye de bağlanabilir |
| İşleç Aşırı Yükleme | `a + b sonucu` | geçici | `a + b + c`'de yalnız ara sonuç geçicidir; son sonuç doğrudan `toplam`da kurulur |
| Kalıtım | `taban sinifin islevi` | parça | Tekli ve sanal olmayan kalıtımla sınırlı |
| Sanal Fonksiyonlar ve Sanal Tablolar | `taban referansindan sanal cagri` | kendisi | Değerle atama ve sanal olmayan yıkıcı kapsam dışı |
| Soyut Sınıflar ve Arayüzler | `arayuz uzerinden cagri` | kendisi | Çoklu arayüz kalıtımı bir sonraki derse bırakıldı |
| Çoklu Kalıtım ve Elmas Sorunu | `sanal tabanin alt nesnesi` | parça | Sanal tabana erişimin dolaylılık maliyeti ölçülmedi |
| İstisnalar | `degerle yakalanan istisna` | kopya | `catch(...)` ve `noexcept` içinde fırlatma kapsam dışı |
| Ad Alanları ve Kod Düzeni | `using bildirimiyle anilan nesne` | kendisi | ADL ve `#include` mekaniği kapsam dışı |
| Tip Dönüşüm İşleçleri | `tabana donusturulen referans` | parça | Hatalı `static_cast` aşağı dönüşümü yalnız anlatıldı, çalıştırılmadı |

**5 kendisi · 3 kopya · 4 parça · 2 geçici; var olan 9, yeni 5**

On dört ders boyunca aynı soru on dört farklı sınırda soruldu; cevap hep
işlecin ya da sözdiziminin adına değil, derleyicinin gerçekte ne kurduğuna
bakılarak verildi. Sıradaki kurs bu soruyu bir adım öteye taşıyor: bir nesnenin
sahip olduğu kaynağı ne zaman bıraktığı, bir değeri kopyalamak yerine ne zaman
taşıdığı ve standart kütüphanenin bu taşımayı nasıl kullandığı.

