Üç / 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ı, kopya oluşturucu ve taşıma oluşturucuya ihtiyaç duyduğunuzu öğrenmişsinizdir. 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 üç öğeden herhangi birini tanımlarsanız, üçünü de tanımlamalısınız:
- Yıkıcı
- Kopya oluşturucu
- Kopya atama işleci
Rule of Five, taşıma işlemlerini ekleyerek bunu modern C++ için genişletir:
- Yıkıcı
- Kopya oluşturucu
- Kopya atama işleci
- Taşıma oluşturucusu
- 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. Kopya kurucu
Buffer& operator=(const Buffer& other); // 3. Kopya atama
Buffer(Buffer&& other) noexcept; // 4. Taşıma kurucu
Buffer& operator=(Buffer&& other) noexcept; // 5. Taşıma atama
};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. Bunları derleyicinin 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ı, kopya veya taşıma fonksiyonlarına gerek yok!
};Bu kurallara uymak, bazı işlemler tanımlanmış ancak diğerleri eksik olduğunda ortaya çıkan çift silme, bellek sızıntıları ve sarkan işaretçiler gibi hataları önler.
Görev
KolayDinamik olarak ayrılmış karakter verilerini düzgün şekilde yönetmek için beş özel üye işlevin tamamını uygulayarak Rule of Five kurallarını izleyen bir TextBuffer sınıfı oluşturalım. Bu, sağlam ve kaynak yöneten bir sınıf oluşturmak için kopyalama ve taşıma işlemlerinin birlikte nasıl çalıştığını gösterecektir.
Kodunuzu düzenlemek için iki dosya oluşturacaksınız:
TextBuffer.h: Metni dinamik olarak ayrılmış bir karakter dizisinde depolayan birTextBuffersınıfı tanımlayın. Sınıfınız şunlara sahip olmalıdır:- Özel üyeler: metin içeriği için
char*adlı birdataişaretçisi ve dize uzunluğu için (null sonlandırıcı dahil olmayan) birsize_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 (null değilse) serbest bırakan ve
"TextBuffer destroyed"yazdıran bir yıkıcı - Derin kopyalama gerçekleştiren ve
"TextBuffer copied"yazdıran bir kopya kurucusu - Kendine atamayı ele alan, mevcut verileri temizleyen, derin kopyalama gerçekleştiren ve
"TextBuffer copy-assigned"yazdıran bir kopya atama işleci.*thisdöndürün - Sahipliği devreden ve
noexceptyazdıran,"TextBuffer moved"ile işaretlenmiş bir taşıma kurucusu. Kaynağı geçerli ve boş bir durumda bırakın - Kendine atamayı ele alan, mevcut verileri temizleyen, sahipliği devreden ve
noexceptyazdıran,"TextBuffer move-assigned"ile işaretlenmiş bir taşıma atama işleci.*thisdöndürün - Depolanan metni döndüren bir
getText()yöntemi (data null ise boş dize""döndürün) - Uzunluğu döndüren bir
getLength()yöntemi
- Özel üyeler: metin içeriği için
main.cpp: Beş özel üye işlevin tamamını çalışırken gösterin. Girdiden bir metin dizesi okuyun, ardından:- Girdi metniyle
TextBufferadlı biroriginaloluşturun copiedkaynağındaki kopya kurucusunu kullanarakoriginaloluşturunanothermetniyle"Temporary"oluşturun- Kopya atamasını kullanın:
another = original movedkullanarakoriginalkaynağından taşıma kurucusuylastd::move()oluşturuntargetmetniyle"Target"oluşturun- Taşıma atamasını kullanın:
target = std::move(copied) "--- Final State ---"yazdırın- Her arabellek için — original, copied, moved, another, target —
"original: <text> (length: <len>)"yazdırın
- Girdi metniyle
Taşıma işlemlerinden sonra kaynak nesneler (original ve copied) uzunlukları 0 olan boş metin göstermeli, hedef nesneler ise aktarılan verileri içermelidir. Bu, Rule of Five kurallarını uygulamalı olarak gösterir: güvenli kaynak yönetimi sağlamak için beş işlevin tamamı birlikte çalışır.
<cstring> ve strlen gibi dize işlevleri için strcpy, <utility> için de std::move() ekleyin.
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' adında bir TextBuffer oluştur
// TODO: 'original' üzerinden kopya yapıcı kullanarak 'copied' oluştur
// TODO: "Temporary" metniyle 'another' oluştur
// TODO: Kopya atama kullan: another = original
// TODO: std::move() kullanarak 'original'dan taşıma yapıcısıyla 'moved' oluştur
// TODO: "Target" metniyle 'target' oluştur
// TODO: Taşıma ataması kullan: target = std::move(copied)
// TODO: "--- Final State ---" yazdır
// TODO: Her arabelleğin durumunu şu formatta yazdır:
// "original: <text> (length: <len>)"
// Şunlar için yazdır: original, copied, moved, another, target
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