Atama Operatörü Aşırı Yükleme
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 44 / 104.
Kopyalama atama operatörü (=), mevcut bir nesneyi diğerine atadığınızda çağrılır. Kopyalama kurucusunun (yeni bir nesne oluşturan) aksine, atama operatörü halihazırda var olan ve öncelikle temizlenmesi gereken kaynaklara sahip olabilecek bir nesneyle çalışır.
class Buffer {
int* data;
size_t size;
public:
Buffer(size_t s) : size(s), data(new int[s]) {}
~Buffer() { delete[] data; }
Buffer& operator=(const Buffer& other) {
if (this != &other) { // 1. Kendi kendine atama kontrolü
delete[] data; // 2. Mevcut kaynağı temizle
size = other.size; // 3. Veriyi kopyala
data = new int[size];
for (size_t i = 0; i < size; ++i)
data[i] = other.data[i];
}
return *this; // 4. *this döndür
}
};Kendi kendine atama kontrolü (this != &other) kritiktir. Bu olmadan, b = b; veriyi kopyalamaya çalışmadan önce siler ve tanımsız davranışa neden olur.
Operatör, a = b = c; gibi zincirlemeyi etkinleştirmek için *this ifadesini referans yoluyla döndürür. Bu, yerleşik türlerin nasıl davrandığıyla eşleşir.
Kaynakları yöneten sınıflar için, genellikle hem kopyalama hem de taşıma atama operatörlerini uygulayacaksınız:
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data; // Mevcut kaynağı temizle
data = other.data; // Kaynağı çal
size = other.size;
other.data = nullptr; // Kaynağı geçerli bir durumda bırak
other.size = 0;
}
return *this;
}Taşıma atama operatörü, kopyalamak yerine sahipliği aktarır ve vec[0] = createBuffer(); gibi işlemleri çok daha verimli hale getirir.
Görev
KolayDinamik olarak ayrılmış bir karakter dizisini yöneten ve mevcut nesneler arasındaki atama işlemlerini düzgün bir şekilde gerçekleştiren bir DynamicString sınıfı oluşturalım. Bu, bellek sızıntıları veya boşta kalan göstericiler (dangling pointers) olmadan dize verilerini bir nesneden diğerine güvenli bir şekilde aktarmanız gereken pratik bir senaryodur.
Kodunuzu düzenlemek için iki dosya oluşturacaksınız:
DynamicString.h: Dinamik bellek kullanarak C stili bir dizeyi saklayan birDynamicStringsınıfı tanımlayın. Sınıfınız şunları yönetmelidir:- Dize verileri için özel (private) bir
char*göstericisi - Uzunluk için özel (private) bir
size_t - Bir
const char*alan ve derin kopya (deep copy) oluşturan bir kurucu (constructor) - Ayrılan belleği düzgün bir şekilde serbest bırakan bir yıkıcı (destructor)
- Kendi kendine atama korumasıyla derin kopya gerçekleştiren bir kopyalama atama operatörü (copy assignment operator)
- Sahipliği verimli bir şekilde aktaran bir taşıma atama operatörü (move assignment operator)
- Dahili dizeyi döndüren bir
c_str()metodu (const) - Dize uzunluğunu döndüren bir
length()metodu (const)
Kopyalama atama operatörünüz dört temel adımı izlemelidir: kendi kendine atamayı kontrol etme, mevcut kaynakları temizleme, yeni verileri kopyalama ve
*thisdöndürme. Taşıma atama operatörü, kaynağın göstericisini çalmalı ve kaynağı geçerli bir boş durumda (0 uzunluğunda nullptr) bırakmalıdır.- Dize verileri için özel (private) bir
main.cpp: Girişten üç dize okuyun (her biri ayrı bir satırda). ÜçDynamicStringnesnesi oluşturun: ilk girişlestr1, ikinci girişlestr2ve üçüncü girişlestr3.Atama operatörlerinizi gösterin:
- Her üç dizenin başlangıç durumunu yazdırın
- Kopyalama atamasını kullanın:
str1 = str2; - Kopyalama atamasından sonra str1 ve str2'yi yazdırın (her ikisi de aynı içeriğe sahip olmalıdır)
- Taşıma atamasını kullanın:
str2 = std::move(str3); - Taşıma atamasından sonra str2'yi ve str3'ün uzunluğunu yazdırın (taşındıktan sonra 0 olmalıdır)
Çıktı formatı:
Initial: str1: <value> str2: <value> str3: <value> After copy (str1 = str2): str1: <value> str2: <value> After move (str2 = std::move(str3)): str2: <value> str3 length: 0
strlen ve strcpy için <cstring> kullanın. Taşıma atama operatörünüzü noexcept olarak işaretleyin. std::move için main.cpp dosyasına <utility> ekleyin. Başlık dosyanızda başlık koruyucularını (header guards) unutmayın.
Kendin dene
#include <iostream>
#include <string>
#include <utility>
#include "DynamicString.h"
using namespace std;
int main() {
// Girdiden üç dize oku
string input1, input2, input3;
getline(cin, input1);
getline(cin, input2);
getline(cin, input3);
// Üç DynamicString nesnesi oluştur
DynamicString str1(input1.c_str());
DynamicString str2(input2.c_str());
DynamicString str3(input3.c_str());
// TODO: Her üç dizenin başlangıç durumunu yazdır
// Biçim:
// Initial:
// str1: <value>
// str2: <value>
// str3: <value>
// TODO: Kopyalama atamasını kullan: str1 = str2;
// TODO: Kopyalama atamasından sonra str1 ve str2'yi yazdır
// Biçim:
// After copy (str1 = str2):
// str1: <value>
// str2: <value>
// TODO: Taşıma atamasını kullan: str2 = std::move(str3);
// TODO: Taşımadan sonra str2'yi ve str3'ün uzunluğunu yazdır
// Biçim:
// After move (str2 = std::move(str3)):
// str2: <value>
// str3 length: 0
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