Mixin melalui CRTP
Termasuk bagian Pemrograman Berorientasi Object dalam perjalanan C++ di Coddy. Pelajaran 77 dari 104.
Curiously Recurring Template Pattern (CRTP) adalah teknik ketika sebuah class mewarisi class dasar template dan meneruskan dirinya sendiri sebagai argumen template. Teknik ini memungkinkan polimorfisme saat kompilasi dan memungkinkan class dasar mengakses anggota class turunan tanpa fungsi virtual.
Struktur dasar CRTP terlihat seperti ini:
template <typename Derived>
class Base {
public:
void interface() {
static_cast<Derived*>(this)->implementation();
}
};
class MyClass : public Base<MyClass> {
public:
void implementation() {
std::cout << "MyClass implementation\n";
}
};CRTP sangat ampuh untuk membuat mixins, yaitu fungsionalitas yang dapat digunakan kembali dan “dicampurkan” ke dalam class. Tidak seperti pewarisan tradisional, mixin menambahkan kemampuan tanpa membuat hierarki yang dalam:
template <typename Derived>
class Printable {
public:
void print() const {
const Derived& self = static_cast<const Derived&>(*this);
std::cout << self.toString() << "\n";
}
};
class Person : public Printable<Person> {
std::string name;
public:
Person(const std::string& n) : name(n) {}
std::string toString() const { return "Person: " + name; }
};
// Penggunaan:
Person p("Alice");
p.print(); // output: Person: AliceKeunggulan utamanya adalah semua pemanggilan method diselesaikan saat kompilasi, sehingga menghilangkan overhead fungsi virtual. Kamu dapat menggabungkan beberapa mixin CRTP untuk menyusun fungsionalitas, sehingga menjadikannya alternatif yang fleksibel untuk polimorfisme saat runtime ketika tipe sudah diketahui saat kompilasi.
Tantangan
MudahMari buat sistem pencatatan yang menggunakan mixin CRTP untuk menambahkan fungsionalitas yang dapat digunakan kembali ke berbagai class tanpa overhead fungsi virtual. Kamu akan membuat dua mixin yang dapat dicampurkan ke class apa pun: satu untuk menghitung instance dan satu untuk menghasilkan representasi string.
Kamu akan menyusun kodemu dalam tiga file:
Mixins.h: Definisikan dua template mixin CRTP yang menyediakan fungsionalitas yang dapat digunakan kembali.Buat template mixin
Countableyang melacak jumlah instance dari class turunan yang ada. Template ini harus memiliki penghitung statis yang bertambah di constructor dan berkurang di destructor. Sediakan method statisgetCount()yang mengembalikan jumlah saat ini.Buat template mixin
Describableyang menyediakan methoddescribe(). Method ini harus menggunakanstatic_castuntuk mengakses class turunan dan memanggil methodgetDescription()miliknya, lalu mencetak hasilnya diikuti baris baru.Ingat bahwa mixin CRTP menggunakan
static_cast<Derived*>(this)untuk mengakses anggota class turunan saat kompilasi.Entities.h: Definisikan dua class entitas yang mewarisi kedua mixin.Buat class
Playeryang mewarisiCountable<Player>danDescribable<Player>. Class ini harus menyimpan nama dan level, serta menyediakan methodgetDescription()yang mengembalikan string dengan format:Player: [name] (Level [level])Buat class
Enemyyang juga mewarisi kedua mixin. Class ini harus menyimpan tipe dan health, serta menyediakan methodgetDescription()yang mengembalikan:Enemy: [type] with [health] HPJangan lupa menginisialisasi penghitung statis untuk setiap class.
main.cpp: Baca empat input (masing-masing pada baris terpisah):- Nama Player (string)
- Level Player (integer)
- Tipe Enemy (string)
- Health Enemy (integer)
Buat Player dan Enemy dengan nilai yang diberikan. Lalu demonstrasikan mixin:
- Cetak
Player count: [count]menggunakan method statisgetCount() - Cetak
Enemy count: [count] - Panggil
describe()pada Player - Panggil
describe()pada Enemy - Buat Player kedua dengan nama "Guest" dan level 1
- Cetak
Player count: [count]lagi untuk menunjukkan jumlah yang diperbarui - Panggil
describe()pada Player kedua
Sebagai contoh, dengan input Hero, 10, Dragon, dan 500:
Player count: 1
Enemy count: 1
Player: Hero (Level 10)
Enemy: Dragon with 500 HP
Player count: 2
Player: Guest (Level 1)Tantangan ini menunjukkan bagaimana mixin CRTP menambahkan fungsionalitas (menghitung dan mendeskripsikan) ke class yang tidak berkaitan tanpa menggunakan fungsi virtual. Player dan Enemy memperoleh kemampuan yang sama dengan mewarisi template mixin yang sama, tetapi masing-masing memiliki penghitung instance terpisah karena template dibuat dengan tipe yang berbeda.
Coba sendiri
#include <iostream>
#include <string>
#include "Entities.h"
using namespace std;
int main() {
// Baca input
string playerName;
int playerLevel;
string enemyType;
int enemyHealth;
cin >> playerName;
cin >> playerLevel;
cin >> enemyType;
cin >> enemyHealth;
// TODO: Buat Player dengan nama dan level yang diberikan
// TODO: Buat Enemy dengan tipe dan kesehatan yang diberikan
// TODO: Cetak "Player count: [count]" menggunakan Player::getCount()
// TODO: Cetak "Enemy count: [count]" menggunakan Enemy::getCount()
// TODO: Panggil describe() pada pemain
// TODO: Panggil describe() pada musuh
// TODO: Buat pemain kedua dengan nama "Guest" dan level 1
// TODO: Cetak "Player count: [count]" lagi
// TODO: Panggil describe() pada pemain kedua
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 Bentuk11Konsep OOP Lanjutan
Komposisi vs PewarisanMixin melalui CRTPIdiom PimplPenghapusan TipeKelas Enum dan Pengetikan KuatPenanganan Pengecualian dalam OOPHierarki Pengecualian Kustom3Konstruktor & 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