Kopya Yapıcı
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 20 / 104.
Kopyalama kurucusu, mevcut bir nesnenin kopyası olarak yeni bir nesne oluşturur. Bir nesneyi aynı türden başka bir nesneden başlatırken, bir nesneyi değere göre aktarırken veya değere göre döndürürken çağrılır.
Kopyalama kurucusu, aynı sınıftan bir nesneye const referans alır. Bunun bir referans olması gerekir: other değerle geçirilseydi, kendisinin de kopyalanması gerekirdi; bu da kopyalama kurucusunun tekrar tekrar, sonsuza dek çağrılmasına yol açardı. const olmasının nedeni, kopyalamanın özgün nesneyi asla değiştirmemesi gerektiğidir:
class Player {
std::string name;
int health;
public:
Player(std::string n, int h) : name(n), health(h) {}
// Kopyalama kurucusu
Player(const Player& other) {
name = other.name;
health = other.health;
}
};
Player original("Hero", 100);
Player copy = original; // Kopyalama kurucusu çağrıldı
Player another(original); // Kopyalama kurucusunu da çağırırBir kopyalama kurucusu tanımlamazsan, derleyici her üyenin değerini doğrudan kopyalayan bir sığ kopya oluşturur. Bu, basit türlerde sorunsuz çalışır ancak sınıfın dinamik belleği yönettiğinde sorunlara yol açar.
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 ayır
for (size_t i = 0; i < size; i++)
data[i] = other.data[i]; // İçeriği kopyala
}
~Buffer() { delete[] data; }
};Özel bir kopyalama kurucusu olmadan, her iki nesne de aynı belleği gösterirdi. Biri yok edildiğinde, diğerinin sarkan bir işaretçisi olurdu. Derin kopya, yeni bellek ayırır ve gerçek verileri kopyalayarak her nesnenin kendi kaynaklarına sahip olmasını sağlar.
Görev
KolaySınıfınız dinamik belleği yönetirken derin kopyalamanın ö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 kendine ait bağımsız belleği olmasını sağlamak için uygun bir kopya oluşturucu 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ızda şunlar bulunmalıdır:- Özel üyeler: Mesajları saklamak için bir dizge dizisine işaretçi, en fazla mesaj sayısını belirten bir
capacityve mevcut mesaj sayısını belirten bircount - Bir kapasite alan, diziyi yığında ayıran ve
countdeğerini 0 olarak başlatan parametreli oluşturucu - Derin kopyalama yapan bir kopya oluşturucu: Yeni bellek ayıran ve kaynak nesnedeki tüm mesajları kopyalayan
- Ayrılmış belleği serbest bırakan ve
"MessageLog destroyed"yazdıran bir yıkıcı - Yer varsa bir 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 üyeler: Mesajları saklamak için bir dizge dizisine işaretçi, en fazla mesaj sayısını belirten bir
main.cpp: Kopya oluşturucunuzun bağımsız bir kopya oluşturduğunu gösterin. Girdiden bir kapasite değeri ve iki mesaj okuyun, ardından:- Girdi kapasitesiyle
originaladında birMessageLogoluşturun - Her iki mesajı da
original'a ekleyin - Kopya oluşturucuyu kullanarak
backupadında bir kopya oluşturun - Üçüncü bir mesajı,
"New message", yalnızcaoriginal'a ekleyin "Original count: <count>"yazdırın"Backup count: <count>"yazdırınbackup'taki tüm mesajları her satıra bir tane gelecek şekilde yazdırın
- Girdi kapasitesiyle
Girdi biçimi şöyle olacaktır:
- İlk satır: kapasite (tam sayı)
- İkinci satır: ilk mesaj (dizge)
- Üçüncü satır: ikinci mesaj (dizge)
Derin kopyalama doğru uygulanırsa, özgün nesnede 3 mesaj bulunurken yedek kopyada 2 mesaj bulunmalıdır: bu, nesnelerin bağımsız belleğe sahip olduğunu kanıtlar. Uygun bir kopya oluşturucu olmadan ikisi de aynı diziyi paylaşır ve birindeki değişiklikler diğerini etkiler!
Üst bilgi dosyanızı main.cpp dosyasına #include "MessageLog.h" kullanarak ekleyin.
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' adlı bir MessageLog oluştur
// TODO: Her iki mesajı da original'a ekle
// TODO: Kopyalama kurucusunu kullanarak 'backup' adlı bir kopya oluştur
// TODO: "New message" mesajını yalnızca original'a ekle
// TODO: "Original count: <count>" metnini yazdır
// TODO: "Backup count: <count>" metnini yazdır
// TODO: backup'taki tüm mesajları, her satıra bir mesaj gelecek şekilde yazdır
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 KonteynerKendi başına pratik yap: Online C++ derleyicisi