Aturan Tiga, Lima, dan Nol
Termasuk bagian Pemrograman Berorientasi Object dalam perjalanan C++ di Coddy. Pelajaran 25 dari 104.
Jika class-mu mengelola sumber daya seperti memori dinamis, kamu telah mempelajari bahwa kamu memerlukan destructor kustom, copy constructor, dan move constructor. Namun, ada prinsip panduan yang membantumu memutuskan 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 konsep ini untuk C++ modern dengan menambahkan operasi pemindahan:
- Destructor
- Konstruktor salin
- Operator penugasan salin
- Konstruktor pemindahan
- Operator penugasan pemindahan
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 penyalinan
Buffer& operator=(const Buffer& other); // 3. Penugasan penyalinan
Buffer(Buffer&& other) noexcept; // 4. Konstruktor pemindahan
Buffer& operator=(Buffer&& other) noexcept; // 5. Penugasan pemindahan
};Kedua operator assignment mengikuti satu pola: cegah assignment ke diri sendiri, lepaskan resource lama, ambil resource 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 sumber daya secara langsung, jangan definisikan fungsi-fungsi ini. Biarkan compiler membuatnya, atau gunakan smart pointer dan container standar yang menangani sumber daya 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 yang menggantung, yang terjadi saat beberapa operasi didefinisikan, tetapi operasi lainnya tidak ada.
Tantangan
MudahMari buat class TextBuffer yang mengikuti Rule of Five: menerapkan 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 menata kodemu:
TextBuffer.h: Definisikan classTextBufferyang menyimpan teks dalam array karakter yang dialokasikan secara dinamis. Class-mu memerlukan:- Anggota private: 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>" - Destruktor yang membebaskan memori (jika tidak null) dan mencetak
"TextBuffer destroyed" - Konstruktor salin yang melakukan penyalinan mendalam dan mencetak
"TextBuffer copied" - Operator penugasan salin yang menangani penugasan ke diri sendiri, membersihkan data yang ada, melakukan penyalinan mendalam, dan mencetak
"TextBuffer copy-assigned". Kembalikan*this - Konstruktor pindah (ditandai dengan
noexcept) yang memindahkan kepemilikan dan mencetak"TextBuffer moved". Biarkan sumber dalam keadaan kosong yang valid - Operator penugasan pindah (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 yang disimpan (kembalikan string kosong""jika data null) - Method
getLength()yang mengembalikan panjang
- Anggota private: pointer
main.cpp: Tunjukkan kelima fungsi anggota khusus saat digunakan. Baca string teks dari input, lalu:- Buat
TextBufferbernamaoriginaldengan teks input - Buat
copiedmenggunakan konstruktor salin darioriginal - Buat
anotherdengan teks"Temporary" - Gunakan penugasan salin:
another = original - Buat
moveddengan membuatnya melalui konstruktor pindah darioriginalmenggunakanstd::move() - Buat
targetdengan teks"Target" - Gunakan penugasan pindah:
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, sedangkan object tujuan menyimpan data yang dipindahkan. Ini menunjukkan Rule of Five: 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 copy constructor dari 'original'
// TODO: Buat 'another' dengan teks "Temporary"
// TODO: Gunakan copy assignment: another = original
// TODO: Buat 'moved' dengan move constructor dari 'original' menggunakan std::move()
// TODO: Buat 'target' dengan teks "Target"
// TODO: Gunakan move assignment: 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