---
title: 'Kopyalama ve Atama'
source: 'https://academia.sh/tr/kurslar/cpp-temelleri/kopyalama-ve-atama'
course: 'C++ Temelleri ve Nesne Tasarımı'
language: tr
updated: '2026-09-26T03:02:45+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Kopyalayan yapıcı ile kopyalayan atamanın ne zaman çağrıldığını ayırt eder'
    - 'Bir sınıfın kendi dizisini alan derin bir kopya ürettiğini ölçer'
    - 'Kendine atama denetiminin neden gerektiğini uygular'
---

# 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.
