奇异递归模板
CRTP通过将基类模板设置为子类,从而实现静态多态(编译器多态),或者扩展接口(委托实现)
要点解析:
1:class Sub : public Base<Sub>,通过模板参数,将子类信息注入到基类,从而实现基类中提前获取子类类型信息
2:static_cast<T*>(this),将基类指针转为模板子类T的指针
3:Base类型是不完整类,不能直接用Sub参与内存布局,但可以在类接口中使用(实际发生调用的时候,模板在编译的时候就辨析了)
4:删除对象,也要使用编译时多态进行删除,避免直接delete
5:模板CRTP基类不能有virtual(因为要编译时多态)
一般用在性能要求非常苛刻的环境中,评测预估性能提升30~40%。 对于一般开发,使用多态更方便阅读。
代码实现
#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include <vector>
using namespace std;
// 基类参数T包含了子类编译时信息
template <typename T> //T一定要子类才能crtp,子类信息注入到父类
class Base {
public:
void process() {
sub()->process_imp(); //编译时分发
}
//如果不实现,类似纯虚函数
void process_imp() {
cout<<"Base::process()"<<endl;
}
//将基类指针转型为子类T的指针,static_cast编译多态,dynamic_cast要求虚函数
T* sub() { return static_cast<T*>(this); } //this就是main中的ps1,转型为子类
~Base() //也可以改为virtual,也会析构正常,但会产生虚函数表,背离了编译时多态
{
//delete sub(); // static_cast<T*>(this); //会死循环,子类调用父类,父类又要调用子类
cout<<"~Base()"<<endl; //必须先析构子类,再析构父类
}
void destroy()
{
delete sub();// static_cast<T*>(this);
}
// T b; //不能,T还是不完整类型,不能参与内存布局
//只能用指针,因为语义上,Base中包含一个sub,而sub又继承base,会导致死循环
};
class Sub1 : public Base<Sub1> {
public:
~Sub1()
{
cout<<"~Sub1"<<endl;
}
void process_imp() {
cout<<"Sub1::process()"<<endl;
}
};
template <typename T>
void invoke(Base<T>* pb)
{
pb->process();
}
int main()
{
Base<Sub1> *ps1=new Sub1(); //声明基类,效果类似虚函数,只是为编译时多态
ps1->process();// process(ps1)
invoke(ps1);
//delete ps1; //不能正确删除,因为是编译时删除,只是删除了基类
ps1->destroy();
}
代码执行
kongcb@tcu-pc:~/testcode/template$ g++ crtp_interface.cpp -o crtp_interface
kongcb@tcu-pc:~/testcode/template$ ./crtp_interface
Sub1::process()
Sub1::process()
~Sub1
~Base()