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ırEğ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.
Görev
KolaySı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 birMessageLogsı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
capacityve mevcut mesaj sayısı için bircount - 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
- Özel (private) üyeler: mesajları saklamak için bir string dizisine işaretçi, maksimum mesaj sayısı için bir
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
originaladında birMessageLogoluşturun - Her iki mesajı da
originalnesnesine ekleyin - Kopya kurucuyu kullanarak
backupadında bir kopya oluşturun - Yalnızca
originalnesnesine üçüncü bir mesaj olan"New message"ekleyin "Original count: <count>"yazdırın"Backup count: <count>"yazdırınbackupiçindeki tüm mesajları her satıra bir tane gelecek şekilde yazdırın
- Giriş kapasitesiyle
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ırEğ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;
}
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
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic Konteyner