概述

使用派生类作为模板参数特化基类。

与多态的区别

  • 多态是动态绑定(运行时绑定),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<子类>接口函数(InterfaceStaticInterface)调用其他成员函数,可以通过使用不同的模板实参来实例化模板实现不同的接口调用效果。

应用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>
{
.....
}

最新文章

  1. 达洛克战记3 即将开服! What&#39;s New!
  2. SQL--存储过程
  3. POJ 2109
  4. IOS中UITableViewCell的重用机制原理
  5. C#操作XML的完整例子——XmlDocument篇(转载,仅做学习之用)
  6. iOS中运用正则表达式
  7. 希尔排序----java实现
  8. Redis系统学习 一、基础知识
  9. [转载]七天学会NodeJS
  10. ES6中函数新增的方式方法
  11. 【EXCEL-折线图】百折不挠 | 用EXCEL画出与众不同的折线图(曲线图)
  12. ogma
  13. 【字】biang
  14. emwin 之 GUI_MessageBox 阻塞特性
  15. NSString 多行的写法和区别
  16. PHP开启伪静态(AppServ服务器)
  17. sqlserver 触发器的运行关键字
  18. java中的中文字符转码技术
  19. map的综合例子
  20. ColorSense颜色检测器

热门文章

  1. 用CSS制作带图标的按钮
  2. ZOJ Problem Set - 1394 Polar Explorer
  3. Oracle 11gR2 RAC修改监听默认端口
  4. 重温Servlet学习笔记--servletContext对象
  5. Android APP压力测试(二)之Monkey信息自动收集脚本
  6. 重启SQL Server——总是好事?
  7. C# NPOI导出Excel和EPPlus导出Excel比较
  8. JSON帮助类
  9. html5数字和颜色输入框
  10. .NET多线程总结和实例介绍