Menu
Coddy logo textTech

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.

challenge icon

Görev

Kolay

Taşı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 bir DataPacket sı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 bir size ve paketi tanımlamak için bir packetId (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, noexcept olarak 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)
  • main.cpp: Girişten bir paket ID'si ve boyutu okuyarak taşıma kurucusunu gösterin. Ardından:
    • Giriş değerleriyle original adında bir DataPacket oluşturun
    • "Original - ID: <id>, Size: <size>, Sum: <sum>" yazdırın
    • std::move() kullanarak original nesnesinden taşıma yoluyla transferred adı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ş 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;
}
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