Menu
CoddyTech

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: Alice

Keunggulan 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.

challenge icon

Tantangan

Mudah

Mari 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 Countable yang melacak jumlah instance dari class turunan yang ada. Template ini harus memiliki penghitung statis yang bertambah di constructor dan berkurang di destructor. Sediakan method statis getCount() yang mengembalikan jumlah saat ini.

    Buat template mixin Describable yang menyediakan method describe(). Method ini harus menggunakan static_cast untuk mengakses class turunan dan memanggil method getDescription() 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 Player yang mewarisi Countable<Player> dan Describable<Player>. Class ini harus menyimpan nama dan level, serta menyediakan method getDescription() yang mengembalikan string dengan format: Player: [name] (Level [level])

    Buat class Enemy yang juga mewarisi kedua mixin. Class ini harus menyimpan tipe dan health, serta menyediakan method getDescription() yang mengembalikan: Enemy: [type] with [health] HP

    Jangan lupa menginisialisasi penghitung statis untuk setiap class.

  • main.cpp: Baca empat input (masing-masing pada baris terpisah):
    1. Nama Player (string)
    2. Level Player (integer)
    3. Tipe Enemy (string)
    4. Health Enemy (integer)

    Buat Player dan Enemy dengan nilai yang diberikan. Lalu demonstrasikan mixin:

    1. Cetak Player count: [count] menggunakan method statis getCount()
    2. Cetak Enemy count: [count]
    3. Panggil describe() pada Player
    4. Panggil describe() pada Enemy
    5. Buat Player kedua dengan nama "Guest" dan level 1
    6. Cetak Player count: [count] lagi untuk menunjukkan jumlah yang diperbarui
    7. 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;
}
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Object

Latihan sendiri: Compiler C++ online