Menu
Coddy logo textTech

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ı Yinelemeli Şablon Deseni (CRTP), bir sınıfın kendisini şablon bağımsız değişkeni olarak geçirerek bir şablon temel sınıftan miras aldığı bir tekniktir. Bu, derleme zamanı çok biçimliliğini mümkün kılar ve temel sınıfların sanal işlevler olmadan türetilmiş sınıf üyelerine erişmesine olanak tanır.

Temel CRTP yapısı şu şekilde 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 — sınıflara "karıştırılabilen" yeniden kullanılabilir işlevler — oluşturmak için özellikle güçlüdür. Geleneksel kalıtımın aksine, mixin'ler derin hiyerarşiler oluşturmadan 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 işlev ek yükünün ortadan kaldırılmasıdır. İşlevselliği oluşturmak için birden çok CRTP mixin'ini birleştirebilir, türler derleme zamanında bilindiğinde bunu çalışma zamanı çok biçimliliğine esnek bir alternatif hâline getirebilirsiniz.

challenge icon

Görev

Kolay

CRTP mixin'lerini kullanarak sanal işlev yükü olmadan farklı sınıflara yeniden kullanılabilir işlevsellik ekleyen bir günlük kaydı sistemi oluşturalım. Herhangi bir sınıfa “karıştırılabilecek” iki mixin oluşturacaksın: biri örnekleri saymak, diğeri dize gösterimleri oluşturmak 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ıfın kaç örneğinin bulunduğunu izleyen bir Countable mixin şablonu oluştur. Yapıcısında artırılan ve yıkıcısında azaltılan statik bir sayacı olmalı. Mevcut sayıyı döndüren statik bir getCount() yöntemi sağla.

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

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

    Her iki mixin'den de kalıtım alan bir Enemy sınıfı oluştur. Bir tür ve can değeri saklamalı ve şunu döndüren bir getDescription() yöntemi sağlamalıdır: 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. Player adı (dize)
    2. Player seviyesi (tam sayı)
    3. Enemy türü (dize)
    4. Enemy canı (tam sayı)

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

    1. Statik Player count: [count] yöntemini kullanarak getCount() yazdır
    2. Enemy count: [count] yazdır
    3. Player üzerinde describe() çağır
    4. Enemy üzerinde describe() çağır
    5. Adı "Guest" ve seviyesi 1 olan ikinci bir Player oluştur
    6. Güncellenmiş sayıyı göstermek için tekrar Player count: [count] yazdır
    7. İkinci Player ü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 görev, CRTP mixin'lerinin sanal işlevler kullanmadan ilgisiz sınıflara işlevsellik (sayma ve açıklama) nasıl 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 örneklendirildiği için her biri kendi ayrı örnek sayacını korur.

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 isim ve seviye ile bir Player oluşturun
    
    // TODO: Verilen tür ve sağlık ile bir Enemy oluşturun
    
    // TODO: Player::getCount() kullanarak "Player count: [count]" yazdırın
    
    // TODO: Enemy::getCount() kullanarak "Enemy count: [count]" yazdırın
    
    // TODO: Oyuncu üzerinde describe() metodunu çağırın
    
    // TODO: Düşman üzerinde describe() metodunu çağırın
    
    // TODO: "Guest" isminde ve 1 seviyesinde ikinci bir oyuncu oluşturun
    
    // TODO: "Player count: [count]" ifadesini tekrar yazdırın
    
    // TODO: İkinci oyuncu üzerinde describe() metodunu çağırın
    
    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