Menu
CoddyTech

CRTP ile Mixin Yapıları

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası. Ders 77 / 104.

Merak Uyandırıcı Tekrarlayan Şablon Deseni (CRTP), bir sınıfın kendisini şablon argümanı olarak geçirerek bir şablon temel sınıfından kalıtım aldığı bir tekniktir. Bu, derleme zamanında çok biçimliliği mümkün kılar ve temel sınıfların sanal işlevler olmadan türetilmiş sınıf üyelerine erişmesini sağlar.

Temel CRTP yapısı şöyle görünür:

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, mixin'ler oluşturmak için özellikle güçlüdür; bunlar sınıflara "karıştırılabilen" yeniden kullanılabilir işlevlerdir. Geleneksel kalıtımdan farklı olarak mixin'ler, derin hiyerarşiler oluşturmadan yeni yetenekler ekler:

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; }
};

// Kullanım:
Person p("Alice");
p.print();  // Çıktı: Person: Alice

Temel avantajı, tüm metot çağrılarının derleme zamanında çözümlenmesi ve sanal fonksiyon yükünün ortadan kaldırılmasıdır. İşlevselliği bir araya getirmek için birden fazla CRTP mixin’ini birleştirebilirsin; böylece türlerin derleme zamanında bilindiği durumlarda çalışma zamanı çok biçimliliğine esnek bir alternatif elde edersin.

challenge icon

Görev

Kolay

Sanal işlev yükü olmadan farklı sınıflara yeniden kullanılabilir işlevsellik eklemek için CRTP mixin'lerini kullanan bir günlük kaydı sistemi oluşturalım. Herhangi bir sınıfa “karıştırılabilen” iki mixin oluşturacaksın: biri örnekleri saymak, diğeri dizge gösterimleri üretmek için.

Kodunu üç dosyaya ayıracaksın:

  • Mixins.h: Yeniden kullanılabilir işlevsellik sağlayan iki CRTP mixin şablonu tanımla.

    Türetilmiş bir sınıftan kaç örnek bulunduğunu izleyen bir Countable mixin şablonu oluştur. Yapıcısında artan, yıkıcısında azalan bir statik sayacı olmalı. Geçerli sayıyı döndüren bir getCount() statik yöntemi sağla.

    Bir describe() yöntemi sağlayan Describable mixin şablonu oluştur. Bu yöntem, türetilmiş sınıfa erişmek ve onun getDescription() yöntemini çağırmak için static_cast kullanmalı, ardından sonucu yeni satırla birlikte yazdırmalı.

    CRTP mixin'lerinin, derleme zamanında türetilmiş sınıfın üyelerine erişmek için static_cast<Derived*>(this) kullandığını unutma.

  • Entities.h: Her iki mixin'den de kalıtım alan iki varlık sınıfı tanımla.

    Hem Countable<Player> hem de Describable<Player> sınıflarından kalıtım alan bir Player sınıfı oluştur. Bu sınıf bir ad ve seviye saklamalı ve şu biçimde bir dizge döndüren getDescription() yöntemi sağlamalı: Player: [name] (Level [level])

    Her iki mixin'den de kalıtım alan bir Enemy sınıfı oluştur. Bu sınıf bir tür ve can değeri saklamalı ve şu değeri döndüren bir getDescription() yöntemi sağlamalı: Enemy: [type] with [health] HP

    Her sınıf için statik sayacı başlatmayı unutma.

  • main.cpp: Dört girdi oku (her biri ayrı bir satırda):
    1. Oyuncu adı (dizge)
    2. Oyuncu seviyesi (tamsayı)
    3. Düşman türü (dizge)
    4. Düşman canı (tamsayı)

    Verilen değerlerle bir Player ve bir Enemy oluştur. Ardından mixin'leri göster:

    1. Statik getCount() yöntemini kullanarak Player count: [count] yazdır
    2. Enemy count: [count] yazdır
    3. Oyuncu üzerinde describe() çağır
    4. Düşman üzerinde describe() çağır
    5. "Guest" adı ve 1 seviyesiyle ikinci bir oyuncu oluştur
    6. Güncel sayıyı göstermek için tekrar Player count: [count] yazdır
    7. İkinci oyuncu üzerinde describe() çağır

Örneğin, girdiler Hero, 10, Dragon ve 500 olduğunda:

Player count: 1
Enemy count: 1
Player: Hero (Level 10)
Enemy: Dragon with 500 HP
Player count: 2
Player: Guest (Level 1)

Bu meydan okuma, CRTP mixin'lerinin sanal işlevler kullanmadan ilgisiz sınıflara işlevsellik (sayma ve açıklama) eklediğini gösterir. Hem Player hem de Enemy, aynı mixin şablonlarından kalıtım alarak aynı yetenekleri kazanır; ancak şablon farklı türlerle örneklendiği için her biri kendi ayrı örnek sayacını tutar.

Kendin dene

#include <iostream>
#include <string>
#include "Entities.h"

using namespace std;

int main() {
    // Girdileri oku
    string playerName;
    int playerLevel;
    string enemyType;
    int enemyHealth;
    
    cin >> playerName;
    cin >> playerLevel;
    cin >> enemyType;
    cin >> enemyHealth;
    
    // TODO: Verilen ad ve seviyeye sahip bir Player oluştur
    
    // TODO: Verilen tür ve can değerine sahip bir Enemy oluştur
    
    // TODO: Player::getCount() kullanarak "Player count: [count]" yazdır
    
    // TODO: Enemy::getCount() kullanarak "Enemy count: [count]" yazdır
    
    // TODO: Oyuncu için describe() çağır
    
    // TODO: Düşman için describe() çağır
    
    // TODO: Adı "Guest" ve seviyesi 1 olan ikinci bir oyuncu oluştur
    
    // TODO: "Player count: [count]" ifadesini tekrar yazdır
    
    // TODO: İkinci oyuncu için describe() çağır
    
    return 0;
}
quiz iconKendini test et

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

Kendi başına pratik yap: Online C++ derleyicisi