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: AliceTemel 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.
Görev
KolayCRTP 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
Countablemixin ş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 birgetCount()yöntemi sağla.Bir
Describableyöntemi sağlayan birdescribe()mixin şablonu oluştur. Bu yöntem, türetilmiş sınıfa erişmek ve onunstatic_castyöntemini çağırmak içingetDescription()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
Playerhem deCountable<Player>sınıflarından kalıtım alan birDescribable<Player>sınıfı oluştur. Bir ad ve seviye saklamalı ve şu biçimde bir dize döndüren birgetDescription()yöntemi sağlamalıdır:Player: [name] (Level [level])Her iki mixin'den de kalıtım alan bir
Enemysınıfı oluştur. Bir tür ve can değeri saklamalı ve şunu döndüren birgetDescription()yöntemi sağlamalıdır:Enemy: [type] with [health] HPHer sınıf için statik sayacı başlatmayı unutma.
main.cpp: Dört girdi oku (her biri ayrı bir satırda):- Player adı (dize)
- Player seviyesi (tam sayı)
- Enemy türü (dize)
- Enemy canı (tam sayı)
Verilen değerlerle bir Player ve bir Enemy oluştur. Ardından mixin'leri göster:
- Statik
Player count: [count]yöntemini kullanarakgetCount()yazdır Enemy count: [count]yazdır- Player üzerinde
describe()çağır - Enemy üzerinde
describe()çağır - Adı "Guest" ve seviyesi 1 olan ikinci bir Player oluştur
- Güncellenmiş sayıyı göstermek için tekrar
Player count: [count]yazdır - İ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;
}
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
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı11İleri Düzey OOP Kavramları
Kompozisyon ve Kalıtım KarşılaştırmasıCRTP ile Mixin YapılarıPimpl İdiyomuTür Silme (Type Erasure)Enum Sınıfları ve Güçlü TiplendirmeOOP'de İstisna YönetimiÖzel İstisna Hiyerarşileri3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic KonteynerKendi başına pratik yap: Online C++ derleyicisi