Yükleniyor...

C++ Copy Constructor ve Assignment Operator

C++ copy constructor ve assignment operator kullanımını öğrenin. Kopyalama semantiği ve nesne yönetimi örneklerle anlatılıyor.

C++’ta nesnelerin birbirine atanması veya kopyalanması sırasında iki özel mekanizma devreye girer: Copy Constructor ve Assignment Operator (=). Her ikisi de nesneler arası veri aktarımı yapar, ancak zamanlama ve kullanım amaçları farklıdır. Bu makalede farklarını, kullanım örneklerini ve derin kopyalama (deep copy) mantığını öğreneceğiz.


1. Copy Constructor Nedir?

Copy constructor, yeni bir nesne oluşturulurken mevcut bir nesneden kopya almak için çağrılan özel yapıcı metottur.


class Book {
private:
    string *title;

public:
    Book(string t) {
        title = new string(t);
        cout << "Constructor çağrıldı." << endl;
    }

    // Copy Constructor
    Book(const Book &other) {
        title = new string(*other.title); // Derin kopya
        cout << "Copy constructor çağrıldı." << endl;
    }

    ~Book() {
        delete title;
        cout << "Destructor çağrıldı." << endl;
    }

    void Show() const { cout << "Kitap: " << *title << endl; }
};

Kullanım:


int main() {
    Book b1("C++ Programlama");
    Book b2 = b1; // Copy constructor çağrılır

    b1.Show();
    b2.Show();
}

Çıktı:


Constructor çağrıldı.
Copy constructor çağrıldı.
Kitap: C++ Programlama
Kitap: C++ Programlama
Destructor çağrıldı.
Destructor çağrıldı.

Bu durumda her nesne kendine ait ayrı bir bellek alanına sahiptir (derin kopya). Eğer sadece adres kopyalanırsa (shallow copy), iki nesne aynı alanı paylaşır ve destructor çağrıldığında “çifte delete” hatası oluşur.


2. Assignment Operator (=) Nedir?

Assignment operator, daha önce oluşturulmuş bir nesneye başka bir nesnenin değerlerini atamak için kullanılır. Yani nesne zaten vardır; sadece içeriği değiştirilir.


class Book {
private:
    string *title;

public:
    Book(string t) {
        title = new string(t);
        cout << "Constructor çağrıldı." << endl;
    }

    // Assignment Operator (kopya atama operatörü)
    Book& operator=(const Book &other) {
        cout << "Assignment operator çağrıldı." << endl;

        // Self-assignment kontrolü
        if (this == &other)
            return *this;

        // Eski belleği sil
        delete title;

        // Yeni kopya oluştur
        title = new string(*other.title);

        return *this;
    }

    ~Book() {
        delete title;
        cout << "Destructor çağrıldı." << endl;
    }

    void Show() const { cout << "Kitap: " << *title << endl; }
};

Kullanım:


int main() {
    Book b1("Modern C++");
    Book b2("Python Rehberi");

    b2 = b1; // Assignment operator çağrılır
    b1.Show();
    b2.Show();
}

Çıktı:


Constructor çağrıldı.
Constructor çağrıldı.
Assignment operator çağrıldı.
Kitap: Modern C++
Kitap: Modern C++
Destructor çağrıldı.
Destructor çağrıldı.

Burada her iki nesne farklı bellek adreslerine sahiptir, ancak içerikleri aynıdır. Eski veriler güvenli biçimde silinip yenisiyle değiştirilmiştir.


3. Shallow Copy (Yüzeysel Kopya) Sorunu

Eğer copy constructor veya assignment operator tanımlanmazsa, C++ derleyicisi otomatik olarak bir “yüzeysel kopya” oluşturur. Bu durumda yalnızca adres kopyalanır, verinin kendisi paylaşılır.


class ShallowBook {
public:
    string *title;

    ShallowBook(string t) {
        title = new string(t);
    }

    ~ShallowBook() {
        delete title;
    }
};

int main() {
    ShallowBook b1("C++ Primer");
    ShallowBook b2 = b1; // Otomatik shallow copy

    cout << *b1.title << endl;
    cout << *b2.title << endl; // Aynı bellek alanı!

} // Destructor iki kez delete yapar → çökme

Bu nedenle dinamik bellek kullanan sınıflarda kopya constructor ve assignment operator mutlaka manuel tanımlanmalıdır.


4. Deep Copy (Derin Kopya) ile Güvenli Kullanım

Derin kopyalama, verinin kendisini belleğin yeni bir alanına kopyalar. Bu sayede her nesne kendi belleğini yönetir.


class DeepBook {
private:
    string *title;

public:
    DeepBook(string t) {
        title = new string(t);
    }

    // Derin kopya constructor
    DeepBook(const DeepBook &other) {
        title = new string(*other.title);
    }

    // Derin kopya assignment operator
    DeepBook& operator=(const DeepBook &other) {
        if (this == &other) return *this;
        delete title;
        title = new string(*other.title);
        return *this;
    }

    ~DeepBook() {
        delete title;
    }

    void Show() const { cout << *title << endl; }
};

int main() {
    DeepBook b1("Derin Kopya Öğreniyorum");
    DeepBook b2 = b1; // Copy constructor
    DeepBook b3("Boş");
    b3 = b1; // Assignment operator

    b1.Show();
    b2.Show();
    b3.Show();
}

Her nesne kendi belleğini yönetir; destructor her biri için güvenli şekilde çağrılır.


5. Rule of Three (Üçlü Kural)

Eğer bir sınıf:

→ Bu üçünün de birlikte tanımlanması gerekir. Çünkü biri tanımlanıp diğeri bırakılırsa, derleyici otomatik oluşturduklarını kullanır ve bu durum hatalara yol açabilir.

C++11 sonrası: “Rule of Five” kavramı eklenmiştir. Buna move constructor ve move assignment operator da dahildir.


6. Uygulamalı Örnek: Book Kopyalama ve Atama

Aşağıdaki örnek, hem copy constructor hem assignment operator’un birlikte nasıl çalıştığını gösterir.


#include <iostream>
#include <string>
using namespace std;

class Book {
private:
    string *title;

public:
    Book(string t) {
        title = new string(t);
        cout << "Constructor: " << *title << endl;
    }

    // Copy constructor
    Book(const Book &other) {
        title = new string(*other.title);
        cout << "Copy constructor: " << *title << endl;
    }

    // Assignment operator
    Book& operator=(const Book &other) {
        cout << "Assignment operator: " << *other.title << endl;
        if (this == &other) return *this;
        delete title;
        title = new string(*other.title);
        return *this;
    }

    ~Book() {
        cout << "Destructor: " << *title << endl;
        delete title;
    }

    void Show() const { cout << *title << endl; }
};

int main() {
    Book b1("C++ Fundamentals");
    Book b2 = b1;      // Copy constructor
    Book b3("Temporary");
    b3 = b1;           // Assignment operator

    cout << "----- Çıktı -----" << endl;
    b1.Show();
    b2.Show();
    b3.Show();
}

Çıktı:


Constructor: C++ Fundamentals
Copy constructor: C++ Fundamentals
Constructor: Temporary
Assignment operator: C++ Fundamentals
----- Çıktı -----
C++ Fundamentals
C++ Fundamentals
C++ Fundamentals
Destructor: C++ Fundamentals
Destructor: C++ Fundamentals
Destructor: C++ Fundamentals

Böylece hem nesne kopyalama hem de atama güvenli ve kontrollü şekilde yapılmıştır.


7. TL;DR

  • Copy constructor → Yeni nesne oluşturulurken mevcut nesneden kopya alınır.
  • Assignment operator → Mevcut bir nesnenin içeriği başka nesneye atanır.
  • Derin kopya yapılmazsa “çifte delete” hatası oluşur.
  • “Rule of Three” → Destructor, Copy Constructor, Assignment Operator birlikte tanımlanmalıdır.
  • “Rule of Five” (C++11) → Move constructor ve move assignment operator da eklenmiştir.
  • Tüm örnekler Visual Studio 2022 veya GCC 11+ ortamında güvenle çalıştırılabilir.

İlişkili Makaleler