Menu
Coddy logo textTech

Kopya Yapıcı

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 20 / 104.

Bir kopya kurucu (copy constructor), mevcut bir nesnenin kopyası olarak yeni bir nesne oluşturur. Aynı türden başka bir nesneden bir nesne başlattığınızda, bir nesneyi değerle geçirdiğinizde veya bir nesneyi değerle döndürdüğünüzde çağrılır.

Kopya kurucu (copy constructor), aynı sınıftan bir nesneye const referansı alır:

class Player {
    std::string name;
    int health;
public:
    Player(std::string n, int h) : name(n), health(h) {}
    
    // Kopya kurucu
    Player(const Player& other) {
        name = other.name;
        health = other.health;
    }
};

Player original("Hero", 100);
Player copy = original;    // Kopya kurucu çağrıldı
Player another(original);  // Ayrıca kopya kurucuyu çağırır

Eğer bir kopya kurucu tanımlamazsanız, derleyici her bir üyenin değerini doğrudan kopyalayan bir yüzeysel kopya (shallow copy) oluşturur. Bu, basit türler için sorunsuz çalışır, ancak sınıfınız dinamik bellek yönettiğinde sorunlara neden olur.

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    // Derin kopyalama kurucusu
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // Yeni bellek tahsis et
        for (size_t i = 0; i < size; i++)
            data[i] = other.data[i];       // İçeriği kopyala
    }
    
    ~Buffer() { delete[] data; }
};

Özel bir kopya kurucu (copy constructor) olmadan, her iki nesne de aynı belleği işaret eder. Biri yok edildiğinde, diğeri boşta kalan bir göstericiye (dangling pointer) sahip olur. Bir derin kopya (deep copy) yeni bellek ayırır ve gerçek veriyi kopyalayarak her nesnenin kendi kaynaklarına sahip olmasını sağlar.

challenge icon

Görev

Kolay

Sınıfınız dinamik bellek yönettiğinde derin kopyalamanın (deep copying) önemini gösteren bir mesaj geçmişi sistemi oluşturalım. Mesajları dinamik olarak ayrılmış bir dizide saklayan bir MessageLog sınıfı oluşturacak ve her kopyanın kendi bağımsız belleğine sahip olmasını sağlamak için uygun bir kopya kurucu (copy constructor) uygulayacaksınız.

Kodunuzu düzenlemek için iki dosya oluşturacaksınız:

  • MessageLog.h: Dinamik bellek kullanarak bir mesaj koleksiyonunu yöneten bir MessageLog sınıfı tanımlayın. Sınıfınız şunlara sahip olmalıdır:
    • Özel (private) üyeler: mesajları saklamak için bir string dizisine işaretçi, maksimum mesaj sayısı için bir capacity ve mevcut mesaj sayısı için bir count
    • Kapasite alan, diziyi yığında (heap) tahsis eden ve count değerini 0 olarak başlatan parametreli bir kurucu
    • Derin kopya gerçekleştiren bir kopya kurucu — yeni bellek tahsis eden ve kaynak nesnedeki tüm mesajları kopyalayan
    • Tahsis edilen belleği serbest bırakan ve "MessageLog destroyed" yazdıran bir yıkıcı (destructor)
    • Yer varsa mesaj ekleyen bir addMessage(string msg) metodu
    • Mevcut mesaj sayısını döndüren bir getCount() metodu
    • Verilen indeksteki mesajı döndüren bir getMessage(int index) metodu
  • main.cpp: Kopya kurucunuzun bağımsız bir kopya oluşturduğunu gösterin. Girişten bir kapasite değeri ve iki mesaj okuyun, ardından:
    • Giriş kapasitesiyle original adında bir MessageLog oluşturun
    • Her iki mesajı da original nesnesine ekleyin
    • Kopya kurucuyu kullanarak backup adında bir kopya oluşturun
    • Yalnızca original nesnesine üçüncü bir mesaj olan "New message" ekleyin
    • "Original count: <count>" yazdırın
    • "Backup count: <count>" yazdırın
    • backup içindeki tüm mesajları her satıra bir tane gelecek şekilde yazdırın

Giriş formatı şu şekilde olacaktır:

  • Birinci satır: kapasite (tam sayı)
  • İkinci satır: ilk mesaj (string)
  • Üçüncü satır: ikinci mesaj (string)

Derin kopyalamanız doğru şekilde uygulandıysa, backup 2 mesaja sahip olurken original 3 mesaja sahip olmalıdır — bu da bağımsız belleğe sahip olduklarını kanıtlar. Uygun bir kopya kurucu olmadan, her ikisi de aynı diziyi paylaşır ve birindeki değişiklikler diğerini etkilerdi!

Başlık dosyanızı main.cpp içine #include "MessageLog.h" kullanarak dahil edin.

Kopya kağıdı

Bir kopya kurucu (copy constructor), mevcut bir nesnenin kopyası olarak yeni bir nesne oluşturur. Aynı sınıftan bir nesneye const referansı alır:

class Player {
    std::string name;
    int health;
public:
    Player(std::string n, int h) : name(n), health(h) {}
    
    // Kopya kurucu
    Player(const Player& other) {
        name = other.name;
        health = other.health;
    }
};

Player original("Hero", 100);
Player copy = original;    // Kopya kurucu çağrıldı
Player another(original);  // Ayrıca kopya kurucuyu çağırır

Eğer bir kopya kurucu tanımlamazsanız, derleyici her bir üyenin değerini doğrudan kopyalayan bir yüzeysel kopya (shallow copy) oluşturur. Bu, sınıfınız dinamik bellek yönettiğinde sorunlara neden olur.

Bir derin kopya kurucu (deep copy constructor) yeni bellek ayırır ve gerçek verileri kopyalayarak her nesnenin kendi kaynaklarına sahip olmasını sağlar:

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    // Derin kopya kurucu
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // Yeni bellek ayır
        for (size_t i = 0; i < size; i++)
            data[i] = other.data[i];       // İçeriği kopyala
    }
    
    ~Buffer() { delete[] data; }
};

Özel bir kopya kurucu olmadan, her iki nesne de aynı belleği işaret eder ve biri yok edildiğinde sarkan işaretçilere (dangling pointers) neden olur.

Kendin dene

#include <iostream>
#include <string>
#include "MessageLog.h"
using namespace std;

int main() {
    // Girdiyi oku
    int capacity;
    cin >> capacity;
    cin.ignore();
    
    string msg1, msg2;
    getline(cin, msg1);
    getline(cin, msg2);

    // TODO: Girdi kapasitesiyle 'original' adında bir MessageLog oluşturun

    // TODO: Her iki mesajı da original nesnesine ekleyin

    // TODO: Kopya oluşturucu (copy constructor) kullanarak 'backup' adında bir kopya oluşturun

    // TODO: Sadece original nesnesine "New message" ekleyin

    // TODO: "Original count: <count>" yazdırın

    // TODO: "Backup count: <count>" yazdırın

    // TODO: backup içindeki tüm mesajları her satıra bir tane gelecek şekilde yazdırın

    return 0;
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler