Menu
CoddyTech

Üç / Beş / Sıfır Kuralı

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 25 / 104.

Sınıfınız dinamik bellek gibi kaynakları yönettiğinde, özel bir yıkıcıya, kopyalama kurucusuna ve taşıma kurucusuna ihtiyaç duyduğunuzu öğrendiniz. Ancak hangi özel üye işlevlerini uygulayacağınıza karar vermenize yardımcı olan bir yol gösterici ilke vardır: Üç, Beş ve Sıfır Kuralı.

Üç Kuralı şunu belirtir: Bu üç üyeden herhangi birini tanımlarsanız, üçünü de tanımlamalısınız:

  • Yıkıcı
  • Kopyalama kurucusu
  • Kopyalama atama işleci

Beşler Kuralı, taşıma işlemlerini ekleyerek bunu modern C++ için genişletir:

  • Yıkıcı
  • Kopyalama kurucusu
  • Kopyalama atama işleci
  • Taşıma kurucusu
  • Taşıma atama işleci
class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    ~Buffer() { delete[] data; }                          // 1. Yıkıcı
    Buffer(const Buffer& other);                          // 2. Kopyalama kurucusu
    Buffer& operator=(const Buffer& other);               // 3. Kopyalama ataması
    Buffer(Buffer&& other) noexcept;                      // 4. Taşıma kurucusu
    Buffer& operator=(Buffer&& other) noexcept;           // 5. Taşıma ataması
};

Her iki atama işleci de aynı kalıbı izler: kendine atamaya karşı koruma sağlayın, eski kaynağı serbest bırakın, yenisini alın ve *this değerini döndürün:

Buffer& Buffer::operator=(const Buffer& other) {   // kopya ataması
    if (this != &other) {
        delete[] data;
        size = other.size;
        data = new int[size];
        std::copy(other.data, other.data + size, data);
    }
    return *this;
}

Buffer& Buffer::operator=(Buffer&& other) noexcept {   // taşıma ataması
    if (this != &other) {
        delete[] data;
        data = other.data;
        size = other.size;
        other.data = nullptr;
        other.size = 0;
    }
    return *this;
}

Sıfır Kuralı en basit yaklaşımdır: Sınıfınız kaynakları doğrudan yönetmiyorsa bu işlevlerden hiçbirini tanımlamayın. Derleyicinin bunları oluşturmasına izin verin veya kaynakları sizin için yöneten akıllı işaretçiler ve standart kapsayıcılar kullanın.

class Player {
    std::string name;              // std::string kendi belleğini yönetir
    std::vector<int> scores;       // std::vector kendi kaynaklarını yönetir
public:
    Player(std::string n) : name(n) {}
    // Yıkıcı, kopyalama veya taşıma fonksiyonlarına gerek yok!
};

Bu kurallara uymak, bazı işlemler tanımlanıp diğerleri eksik olduğunda ortaya çıkan çift silme, bellek sızıntıları ve sarkan işaretçiler gibi hataları önler.

challenge icon

Görev

Kolay

Dinamik olarak ayrılan karakter verilerini doğru şekilde yönetmek için beş özel üye işlevin tümünü uygulayan, Beşler Kuralı'na uyan bir TextBuffer sınıfı oluşturalım. Bu, kopyalama ve taşıma işlemlerinin sağlam, kaynakları yöneten bir sınıf oluşturmak için birlikte nasıl çalıştığını gösterecek.

Kodunu düzenlemek için iki dosya oluşturacaksın:

  • TextBuffer.h: Metni dinamik olarak ayrılan bir karakter dizisinde saklayan bir TextBuffer sınıfı tanımla. Sınıfın şunlara sahip olmalı:
    • Özel üyeler: Metin içeriği için data adlı bir char* işaretçisi ve dizenin uzunluğu için (sonlandırıcı null karakter hariç) bir size_t length
    • Bir C dizesi (const char*) alan, bellek ayıran, içeriği kopyalayan ve "TextBuffer created: <text>" yazdıran parametreli kurucu
    • Belleği serbest bırakan (null değilse) ve "TextBuffer destroyed" yazdıran bir yıkıcı
    • Derin kopyalama yapan ve "TextBuffer copied" yazdıran bir kopya kurucusu
    • Kendine atamayı işleyen, mevcut verileri temizleyen, derin kopyalama yapan ve "TextBuffer copy-assigned" yazdıran bir kopya atama işleci. *this döndür
    • Sahipliği devreden ve "TextBuffer moved" yazdıran bir taşıma kurucusu (noexcept olarak işaretlenmiş). Kaynağı geçerli ve boş durumda bırak
    • Kendine atamayı işleyen, mevcut verileri temizleyen, sahipliği devreden ve "TextBuffer move-assigned" yazdıran bir taşıma atama işleci (noexcept olarak işaretlenmiş). *this döndür
    • Saklanan metni döndüren bir getText() yöntemi (data null ise boş dize "" döndür)
    • Uzunluğu döndüren bir getLength() yöntemi
  • main.cpp: Beş özel üye işlevin tümünü çalışırken göster. Girdiden bir metin dizesi oku, ardından:
    • Girdi metniyle original adlı bir TextBuffer oluştur
    • original nesnesinden kopya kurucusunu kullanarak copied oluştur
    • "Temporary" metniyle another oluştur
    • Kopya atama işlemini kullan: another = original
    • std::move() kullanarak original nesnesinden taşıma kurucusuyla moved oluştur
    • "Target" metniyle target oluştur
    • Taşıma atama işlemini kullan: target = std::move(copied)
    • "--- Final State ---" yazdır
    • Her arabellek için (original, copied, moved, another, target) "original: <text> (length: <len>)" yazdır

Taşıma işlemlerinden sonra kaynak nesneler (original ve copied) uzunluğu 0 olan boş metin göstermeli; hedef nesneler ise devredilen verileri tutmalıdır. Bu, Beşler Kuralı'nın işleyişini gösterir: güvenli kaynak yönetimi sağlamak için beş işlevin tümü birlikte çalışır.

strlen ve strcpy gibi dize işlevleri için <cstring>, std::move() için de <utility> ekle.

Kendin dene

#include <iostream>
#include <string>
#include <utility>
#include "TextBuffer.h"

using namespace std;

int main() {
    string input;
    getline(cin, input);

    // TODO: Girdi metniyle 'original' adlı bir TextBuffer oluştur

    // TODO: 'original' üzerinden kopyalama kurucusunu kullanarak 'copied' oluştur

    // TODO: "Temporary" metniyle 'another' oluştur

    // TODO: Kopyalama atamasını kullan: another = original

    // TODO: std::move() kullanarak 'original' üzerinden taşıma kurucusuyla 'moved' oluştur

    // TODO: "Target" metniyle 'target' oluştur

    // TODO: Taşıma atamasını kullan: target = std::move(copied)

    // TODO: "--- Final State ---" yazdır

    // TODO: Her tamponun durumunu şu biçimde yazdır:
    // "original: <text> (length: <len>)"
    // Şunlar için yazdır: original, copied, moved, another, target

    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

Kendi başına pratik yap: Online C++ derleyicisi