概述
使用派生類作為模板參數特化基類。
與多態的區別
- 多態是動態綁定(運行時綁定),CRTP是靜態綁定(編譯時綁定)
- 在實現多態時,需要重寫虛函數,因而這是運行時綁定的操作。
- CRTP在編譯期確定通過基類來得到派生類的行為,它通過派生類覆蓋基類成員函數來實現靜態綁定的。
例子1
說明:
- 父類調用直接子類函數的方法:
- 靜態函數成員:this 指針不可見,而對於某一個實例化的繼承層次來說,只有一個靜態類,因此使用Derived::memberfun()實現
- 非靜態函數成員:調用非靜態成員函數時,必須通過對象或者對象指針,因此將this指針進行靜態轉換。static_cast
(this)
- 函數成員的覆蓋 如果子類中有和父類相同名稱的函數(不管是否靜態),父類版本被屏蔽。
- 其他 通過子類的對象或者對象指針調用其成員函數時,總是優先在其定義中尋找可行函數,如果沒有找到,就執行父類的實現版本
crtp.h
#include<iostream>
#include<stddef.h>
using namespace std;
template<class Derived>
struct Base
{
void Interface()
{
cout <<"come from Interface"<<endl;
// 轉換為子類指針,編譯期將綁定至子類方法
static_cast<Derived*>(this)->Implementation();
}
static void StaticInterface()
{
// 編譯期將綁定至子類方法
cout <<"come from StaticInterface"<<endl;
Derived::StaticImplementation();
}
void Implementation()
{
cout <<"Base Implementation"<<endl;
return;
}
static void StaticImplementation()
{
cout << "Base StaticImplementation"<<endl;
return;
}
};
// The Curiously Recurring Template Pattern (CRTP)
struct Derived1 : Base<Derived1>
{
static void StaticImplementation();
};
struct Derived2 : Base<Derived2>
{
void Implementation();
};
crtp.cc
void Derived1::StaticImplementation()
{
cout << "StaticImplementation from Derived1"<<endl;
return;
}
void Derived2::Implementation()
{
cout <<"Implementation from Derived2"<<endl;
return;
}
- test-crtp.cc
int main()
{
cout << "***********************************" << endl;
Derived1 derive1;
Derived2 derive2;
derive1.Implementation();
derive1.StaticImplementation();
derive2.Implementation();
derive2.StaticImplementation();
cout << "***********************************" << endl << endl;
Base<Derived1> base_derive1;
Base<Derived2> base_derive2;
base_derive1.Implementation();
base_derive1.StaticImplementation();
base_derive2.Implementation();
base_derive2.StaticImplementation();
cout << "***********************************" << endl << endl;
base_derive1.StaticInterface();
base_derive1.Interface();
base_derive2.StaticInterface();
base_derive2.Interface();
cout << "***********************************" << endl << endl;
return 0;
}
運行結果如下:
***********************************
Base Implementation
StaticImplementation from Derived1
Implementation from Derived2
Base StaticImplementation
***********************************
Base Implementation
Base StaticImplementation
Base Implementation
Base StaticImplementation
***********************************
come from StaticInterface
StaticImplementation from Derived1
come from Interface
Base Implementation
come from StaticInterface
Base StaticImplementation
come from Interface
Implementation from Derived2
***********************************
總結
- 第一組結果說明:
- 如果子類中有和父類相同名稱的函數(不管是否靜態),父類版本被屏蔽。
- 如果子類中沒有找到成員函數,就執行父類的實現版本
- 第二組結果說明:
- 通過
Base<子類>
對象調用成員函數時,就和通過Base
對象調用成員函數一樣的效果,不管實例化模板使用的模板實參是什么
- 通過
- 第三組結果說明:
- 通過在
Base<子類>
接口函數(Interface
和StaticInterface
)調用其他成員函數,可以通過使用不同的模板實參來實例化模板實現不同的接口調用效果。
- 通過在
應用1:實現計數
統計每個類的對象個數
template <typename T>
struct counter
{
counter(){ objects_created++;objects_alive++;}
virtual ~counter(){--objects_alive;}
static int objects_created;
static int objects_alive;
};
// 類外初始化
template <typename T> int counter<T>::objects_created( 0 );
template <typename T> int counter<T>::objects_alive( 0 );
class X : counter<X>{// ...};
class Y : counter<Y>{ // ...};
//X和Y類都有自己的計數
應用2:實現對象的引用計數
- ns3中simple-ref-count.h
此處m_count是對象的成員變量
template <typename T, typename PARENT = empty, typename DELETER = DefaultDeleter<T> >
class SimpleRefCount : public PARENT
{
public:
SimpleRefCount (): m_count (1){}
inline void Ref (void) const
{
m_count++;
}
inline void Unref (void) const
{
m_count--;
if (m_count == 0)
{
DELETER::Delete (static_cast<T*> (const_cast<SimpleRefCount *> (this)));
}
}
mutable uint32_t m_count;
};
- ns3中某一個需要計數的類
class Object : public SimpleRefCount<Object, ObjectBase, ObjectDeleter>
{
.....
}