Aturan Tiga, Lima, dan Nol
Termasuk bagian Pemrograman Berorientasi Object dalam perjalanan C++ di Coddy. Pelajaran 25 dari 104.
Saat class-mu mengelola sumber daya seperti memori dinamis, kamu sudah belajar bahwa kamu memerlukan destructor, copy constructor, dan move constructor khusus. Namun, ada prinsip panduan yang membantumu menentukan special member function mana yang perlu diimplementasikan: Rule of Three, Five, and Zero.
Aturan Tiga menyatakan: jika kamu mendefinisikan salah satu dari tiga hal ini, kamu sebaiknya mendefinisikan ketiganya:
- Destruktor
- Konstruktor salin
- Operator penugasan salin
Rule of Five memperluas hal ini untuk C++ modern dengan menambahkan operasi pemindahan:
- Destruktor
- Konstruktor salin
- Operator penugasan salin
- Konstruktor pindah
- Operator penugasan pindah
class Buffer {
int* data;
size_t size;
public:
Buffer(size_t s) : size(s), data(new int[s]) {}
~Buffer() { delete[] data; } // 1. Destruktor
Buffer(const Buffer& other); // 2. Konstruktor penyalin
Buffer& operator=(const Buffer& other); // 3. Penugasan salin
Buffer(Buffer&& other) noexcept; // 4. Konstruktor pemindah
Buffer& operator=(Buffer&& other) noexcept; // 5. Penugasan pindah
};Kedua operator penugasan mengikuti satu pola: cegah penugasan ke diri sendiri, lepaskan sumber daya lama, ambil sumber daya baru, lalu kembalikan *this:
Buffer& Buffer::operator=(const Buffer& other) { // penugasan salin
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 { // penugasan pindah
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}Rule of Zero adalah pendekatan paling sederhana: jika class-mu tidak mengelola resource secara langsung, jangan definisikan fungsi-fungsi ini. Biarkan compiler membuatnya, atau gunakan smart pointer dan container standar yang menangani resource untukmu.
class Player {
std::string name; // std::string mengelola memorinya sendiri
std::vector<int> scores; // std::vector mengelola sumber dayanya sendiri
public:
Player(std::string n) : name(n) {}
// Tidak perlu fungsi destructor, copy, atau move!
};Mengikuti aturan ini mencegah bug seperti penghapusan ganda, kebocoran memori, dan pointer menggantung yang terjadi ketika beberapa operasi didefinisikan, tetapi operasi lainnya tidak tersedia.
Tantangan
MudahMari buat class TextBuffer yang mengikuti Aturan Lima: mengimplementasikan kelima fungsi anggota khusus untuk mengelola data karakter yang dialokasikan secara dinamis dengan benar. Ini akan menunjukkan cara operasi penyalinan dan pemindahan bekerja bersama untuk membuat class yang tangguh dan mengelola resource.
Kamu akan membuat dua file untuk mengatur kodemu:
TextBuffer.h: Definisikan classTextBufferyang menyimpan teks dalam array karakter yang dialokasikan secara dinamis. Class-mu memerlukan:- Anggota privat: pointer
char*bernamadatauntuk konten teks, dansize_t lengthuntuk panjang string (tidak termasuk terminator null) - Konstruktor berparameter yang menerima C-string (
const char*), mengalokasikan memori, menyalin konten, dan mencetak"TextBuffer created: <text>" - Destructor yang membebaskan memori (jika tidak null) dan mencetak
"TextBuffer destroyed" - Copy constructor yang melakukan salinan mendalam dan mencetak
"TextBuffer copied" - Copy assignment operator yang menangani penugasan ke diri sendiri, membersihkan data yang ada, melakukan salinan mendalam, dan mencetak
"TextBuffer copy-assigned". Kembalikan*this - Move constructor (ditandai dengan
noexcept) yang memindahkan kepemilikan dan mencetak"TextBuffer moved". Biarkan sumber dalam keadaan kosong yang valid - Move assignment operator (ditandai dengan
noexcept) yang menangani penugasan ke diri sendiri, membersihkan data yang ada, memindahkan kepemilikan, dan mencetak"TextBuffer move-assigned". Kembalikan*this - Method
getText()yang mengembalikan teks tersimpan (kembalikan string kosong""jika data null) - Method
getLength()yang mengembalikan panjangnya
- Anggota privat: pointer
main.cpp: Tunjukkan kelima fungsi anggota khusus saat digunakan. Baca string teks dari input, lalu:- Buat
TextBufferbernamaoriginaldengan teks input - Buat
copiedmenggunakan copy constructor darioriginal - Buat
anotherdengan teks"Temporary" - Gunakan copy assignment:
another = original - Buat
moveddengan melakukan move construction darioriginalmenggunakanstd::move() - Buat
targetdengan teks"Target" - Gunakan move assignment:
target = std::move(copied) - Cetak
"--- Final State ---" - Cetak
"original: <text> (length: <len>)"untuk setiap buffer: original, copied, moved, another, target
- Buat
Setelah pemindahan, object sumber (original dan copied) seharusnya menampilkan teks kosong dengan panjang 0, sementara object tujuan menyimpan data yang dipindahkan. Ini menunjukkan Aturan Lima saat digunakan: kelima fungsi bekerja bersama untuk memastikan pengelolaan resource yang aman.
Sertakan <cstring> untuk fungsi string seperti strlen dan strcpy, serta <utility> untuk std::move().
Coba sendiri
#include <iostream>
#include <string>
#include <utility>
#include "TextBuffer.h"
using namespace std;
int main() {
string input;
getline(cin, input);
// TODO: Buat TextBuffer bernama 'original' dengan teks input
// TODO: Buat 'copied' menggunakan konstruktor penyalinan dari 'original'
// TODO: Buat 'another' dengan teks "Temporary"
// TODO: Gunakan penugasan salin: another = original
// TODO: Buat 'moved' menggunakan konstruktor pemindahan dari 'original' dengan std::move()
// TODO: Buat 'target' dengan teks "Target"
// TODO: Gunakan penugasan pindah: target = std::move(copied)
// TODO: Cetak "--- Final State ---"
// TODO: Cetak keadaan setiap buffer dalam format ini:
// "original: <text> (length: <len>)"
// Cetak untuk: original, copied, moved, another, target
return 0;
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Object
1Dasar-Dasar OOP
File EksternalBuild dan Kompilasi C++File Header dan File SourceNamespace dan CakupanPengantar OOP dalam C++class vs objectPointer 'this'method (Fungsi Anggota)Atribut (Anggota Data)Dasar Ctor dan DtorRingkasan: Kalkulator Sederhana4Properti class
Anggota instance vs staticGetter dan setterFungsi anggota constKata kunci mutableMethod dan variabel staticFungsi dan class friendRangkuman, Pengelola Rekening Bank7Pewarisan
Pewarisan DasarTingkat Akses pada PewarisanUrutan Pemanggilan Ctor & DtorPenimpaan methodFungsi Virtual & VTablePewarisan BergandaPewarisan VirtualRangkuman: Hierarki Karyawan2Manajemen Memori
Memori Stack vs HeapPointer dan ReferenceMemori Dinamis (new/delete)Smart Pointer di C++RAII di C++Rangkuman: Pengelola Array Dinamis5Enkapsulasi
Penentu Akses dalam C++Penentu Akses secara MendalamPenyembunyian Informasistruct vs classNested dan Inner classRangkuman, Sistem Catatan Siswa8Polimorfisme
Polimorfisme saat Compile vs RuntimeOverloading FungsiMeninjau Kembali Fungsi VirtualFungsi Virtual MurniKelas AbstrakDesain Interface di C++Casting Dinamis & RTTIRangkuman: Kalkulator Bentuk3Konstruktor & Destruktor
Konstruktor DefaultKonstruktor BerparameterKonstruktor SalinKonstruktor PindahDaftar Inisialisasi KonstruktorKonstruktor DelegasiKupas Tuntas DestruktorAturan Tiga, Lima, dan NolRangkuman: class String6Overloading Operator
Pengantar Overloading OperatorOverloading Operator AritmetikaOverloading Operator PerbandinganOperator StreamOverloading Operator PenugasanOverloading Operator [] dan ()Operator Konversi TipeRingkasan, class Matrix9Template
Template FungsiTemplate classSpesialisasi TemplateTemplate VariadikDasar SFINAE & Type TraitsRangkuman, Container GenerikLatihan sendiri: Compiler C++ online