Taşıma Yapıcısı
Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 21 / 104.
Bir taşıma kurucusu (move constructor), kaynakları kopyalamak yerine geçici bir nesneden aktarır. Bir kopya kurucusu verileri çoğaltırken, bir taşıma kurucusu kaynakları "çalar" ve kaynak nesneyi geçerli ancak boş bir durumda bırakır. Bu, kaynak nesne zaten yok edilmek üzereyken maliyetli derin kopyalamalardan kaçınmayı sağlar.
Taşıma kurucusu (move constructor), && ile gösterilen bir rvalue referansı alır:
class Buffer {
int* data;
size_t size;
public:
Buffer(size_t s) : size(s), data(new int[s]) {}
// Taşıma kurucusu
Buffer(Buffer&& other) noexcept {
data = other.data; // İşaretçiyi çal
size = other.size;
other.data = nullptr; // Kaynağı geçerli bir durumda bırak
other.size = 0;
}
~Buffer() { delete[] data; }
};Kopyalamadan temel fark: yeni bellek tahsis etmeyiz. Sadece mevcut belleğin sahipliğini alırız ve kaynağın işaretçisini nullptr olarak ayarlarız, böylece yıkıcısı verilerimizi silmez.
Taşıma kurucuları, geçici nesnelerden başlatma yapıldığında veya std::move() kullanıldığında çağrılır:
Buffer createBuffer() {
return Buffer(1000); // Taşıma kurucusu kullanıldı (dönüş değeri)
}
Buffer b1(1000);
Buffer b2(std::move(b1)); // Açık taşıma - b1 artık boşMümkün olduğunda taşıma kurucularını (move constructors) noexcept olarak işaretleyin. Bu, derleyiciye işlemin istisna fırlatmayacağını bildirir ve std::vector gibi konteynerlerde önemli optimizasyonları etkinleştirir.
Görev
KolayTaşıma kurucularının (move constructors) kaynak sahipliğini nasıl verimli bir şekilde devrettiğini gösteren bir veri paketi sistemi oluşturalım. Dinamik olarak ayrılmış bir bayt dizisini yöneten bir DataPacket sınıfı oluşturacak ve veriyi kopyalamak yerine "çalan" bir taşıma kurucusu uygulayacaksınız.
Kodunuzu düzenlemek için iki dosya oluşturacaksınız:
DataPacket.h: Tam sayı verilerinden oluşan bir yükü (payload) yöneten birDataPacketsınıfı tanımlayın. Sınıfınız şunlara sahip olmalıdır:- Özel (private) üyeler: bir tam sayı dizisine işaretçi (
payload), eleman sayısı için birsizeve paketi tanımlamak için birpacketId(string) - Bir paket ID'si ve boyutu alan, diziyi tahsis eden ve onu 0'dan size-1'e kadar olan değerlerle dolduran parametreli bir kurucu.
"Packet <id> created with size <size>"yazdırın - Kaynak nesneden yükün sahipliğini devralan,
noexceptolarak işaretlenmiş bir taşıma kurucusu."Packet <id> moved"yazdırın. Kaynak nesneyi geçerli bir boş durumda (nullptr, size 0) bırakmayı unutmayın - İşaretçi null değilse belleği serbest bırakan ve
"Packet <id> destroyed"yazdıran bir yıkıcı (destructor) - Mevcut boyutu döndüren bir
getSize()metodu - Paket ID'sini döndüren bir
getId()metodu - Yükteki tüm elemanların toplamını döndüren bir
getSum()metodu (yük null ise 0 döndürün)
- Özel (private) üyeler: bir tam sayı dizisine işaretçi (
main.cpp: Girişten bir paket ID'si ve boyutu okuyarak taşıma kurucusunu gösterin. Ardından:- Giriş değerleriyle
originaladında birDataPacketoluşturun "Original - ID: <id>, Size: <size>, Sum: <sum>"yazdırınstd::move()kullanarakoriginalnesnesinden taşıma yoluylatransferredadında yeni bir paket oluşturun"After move:"yazdırın"Original - ID: <id>, Size: <size>, Sum: <sum>"yazdırın"Transferred - ID: <id>, Size: <size>, Sum: <sum>"yazdırın
- Giriş değerleriyle
Giriş formatı şu şekilde olacaktır:
- Birinci satır: paket ID'si (string)
- İkinci satır: boyut (tam sayı)
Taşıma işleminden sonra, orijinal paketin boyutu 0 ve toplamı 0 olmalıdır (çünkü verileri devredilmiştir), aktarılan paket ise tüm orijinal verilere sahip olmalıdır. Bu, taşıma semantiğinin (move semantics) sadece işaretçi sahipliğini devrederek maliyetli derin kopyalamalardan (deep copies) nasıl kaçındığını gösterir.
Başlık dosyanızı main.cpp içine #include "DataPacket.h" kullanarak dahil edin ve std::move() için <utility> kütüphanesini eklemeyi unutmayın.
Kendin dene
#include <iostream>
#include <string>
#include <utility>
#include "DataPacket.h"
using namespace std;
int main() {
// Girdiyi oku
string packetId;
int size;
cin >> packetId;
cin >> size;
// TODO: Giriş değerleriyle 'original' adında bir DataPacket oluşturun
// TODO: "Original - ID: <id>, Size: <size>, Sum: <sum>" yazdırın
// TODO: original nesnesini taşıyarak 'transferred' adında yeni bir paket oluşturun
// İpucu: std::move() kullanın
// TODO: "After move:" yazdırın
// TODO: "Original - ID: <id>, Size: <size>, Sum: <sum>" yazdırın
// TODO: "Transferred - ID: <id>, Size: <size>, Sum: <sum>" 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