---
title: Kalıtım
source: 'https://academia.sh/tr/kurslar/cpp-temelleri/kalitim'
course: 'C++ Temelleri ve Nesne Tasarımı'
language: tr
updated: '2026-09-26T03:02:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Bir türetilmiş sınıfın taban alt nesnesini nesnenin bir parçası olarak tanımlar'
    - 'Taban ve türetilmiş nesnenin aynı adresi paylaştığını adres karşılaştırmasıyla ölçer'
    - 'Kurucu ve yıkıcı çağrı sırasının tek bir nesnenin iki aşaması olduğunu gözlemler'
---

# Kalıtım

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ığı.
