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: AliceTemel 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.
Görev
KolaySanal 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
Countablemixin ş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 birgetCount()statik yöntemi sağla.Bir
describe()yöntemi sağlayanDescribablemixin şablonu oluştur. Bu yöntem, türetilmiş sınıfa erişmek ve onungetDescription()yöntemini çağırmak içinstatic_castkullanmalı, 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 deDescribable<Player>sınıflarından kalıtım alan birPlayersınıfı oluştur. Bu sınıf bir ad ve seviye saklamalı ve şu biçimde bir dizge döndürengetDescription()yöntemi sağlamalı:Player: [name] (Level [level])Her iki mixin'den de kalıtım alan bir
Enemysınıfı oluştur. Bu sınıf bir tür ve can değeri saklamalı ve şu değeri döndüren birgetDescription()yöntemi sağlamalı: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):- Oyuncu adı (dizge)
- Oyuncu seviyesi (tamsayı)
- Düşman türü (dizge)
- Düşman canı (tamsayı)
Verilen değerlerle bir Player ve bir Enemy oluştur. Ardından mixin'leri göster:
- Statik
getCount()yöntemini kullanarakPlayer count: [count]yazdır Enemy count: [count]yazdır- Oyuncu üzerinde
describe()çağır - Düşman üzerinde
describe()çağır - "Guest" adı ve 1 seviyesiyle ikinci bir oyuncu oluştur
- Güncel sayıyı göstermek için tekrar
Player count: [count]yazdır - İ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;
}
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