概述

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

与多态的区别

  • 多态是动态绑定(运行时绑定),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>
{
.....
}

c++ 奇特的递归模板模式(CRTP)的更多相关文章

  1. 模板与继承之艺术——奇特的递归模板模式(CRTP)

    一.什么是CRTP 奇特的模板递归模式(Curiously Recurring Template Pattern)即将派生类本身作为模板参数传递给基类. template<typename T& ...

  2. C++奇异递归模板模式

    虚函数的问题 虚函数的主要问题是性能开销比较大,一个虚函数调用可能需要花费数倍于非虚函数调用的时间,尤其是当非虚函数被声明为inline时(注意,虚函数不能被内联). CRTP介绍 CRTP的全称是C ...

  3. JAVA设计模式之模板模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述模板方法(Template Method)模式的: 模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式 ...

  4. Java设计模式之模板模式(Template )

    前言: 最近学习了Glide开源图片缓存框架,在学习到通过使用ModelLoader自定义数据源的时候,Glide巧妙的使用了Java的模板模式来对外暴露处理不同的Url数据源,今天来学习总结一下模板 ...

  5. Java设计模式(七) 模板模式

    [模板模式]在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤. 1,定义模板类 package com.pattern ...

  6. 模板模式与策略模式/template模式与strategy模式/行为型模式

    模板模式 模版模式,又被称为模版方法模式,它可以将工作流程进行封装,并且对外提供了个性化的控制,但主流程外界不能修改,也就是说,模版方法模式中,将工作的主体架构规定好,具体类可以根据自己的需要,各自去 ...

  7. 12. 星际争霸之php设计模式--模板模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  8. 模板模式(C++) 【转】

    模板模式(template)在面向对象系统的设计和开发过程中,一定会有这样的情况:对于一些功能,在不同的对象身上展示不同的作用,但是功能的框架是一样的,这就是模板(template)模式的用武之地,我 ...

  9. Head First 设计模式系列之一----模板模式(java版)

    开篇序言:四人帮的设计模式对于我这个菜鸟看着打瞌睡,后面果断买了一本head first的,感觉还可以像看报纸似的,花了一个寒假的晚上看了大半,确实内容也挺吸引人的,讲的很风趣.否则我也不可能,大过年 ...

随机推荐

  1. 如何利用Python生成随机密码

    写了个程序,主要是用来检测MySQL数据库的空密码和弱密码的, 在这里,定义了三类弱密码: 1. 连续数字,譬如123456,在get_weak_num中实现 2. 连续字母,譬如abcdef,在ge ...

  2. 浅谈 php 采用curl 函数库获取网页 cookie 和 带着cookie去访问 网页的方法!!!!

    由于近段时间帮朋友开发一个能够查询正方教务系统的微信公众平台号.有所收获.这里总结下个人经验. 开讲前,先吐槽一下新浪云服务器,一个程序里的   同一个函数  在PC测试可以正常运行,在它那里就会挂的 ...

  3. GIS项目中数据开源、工具开源、开发开源的解决方案

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 摆脱免费地图开发包的约束,拒绝商业地图软件的费用,高效.精确.完备是我 ...

  4. (十九)WebGIS中I查询的原理及设计(包含AGS、GeoServer、Supermap)

    文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 我们在使用arcmap时,经常会用到被称为I查询的工具.具体 ...

  5. Entity FrameWork 365日系列文章源码研究 (1)

    By KMSFan --  此系列的文章只作为自己的读书笔记,不纳入博客园首页. 总结的知识点: 1.DBContext 类 2.Attribute里的属性(NotNull) 3.DbContext实 ...

  6. lamp 环境搭建

    LAMP指的Linux(操作系统).ApacheHTTP 服务器,MySQL(数据库软件)和PHP语言 使用wampserver软件,搭建环境.如下图: 双击程序包,安装最后一步随便选择一个浏览器打开 ...

  7. SQL SERVER 通用分页存储过程,两种用法任你选

    写在前面 从SQLSERVER 2005开始,提供了Row_Number()函数,利用函数生成的Index来处理分页,按照正常的逻辑思维都是传pageIndex和pageSize来完成分页,昨天前端和 ...

  8. 记录下Sublime Text Mac版的快捷键【转】

    推荐使用最新版本的Chrome/Safari或者Firefox浏览器浏览此页,否则Mac按键可能无法正常展示 The Latest Version of Chrome/Safari or Firefo ...

  9. C#+arcengine10.0+SP5实现鹰眼(加载的是mdb数据库中的数据)

    using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...

  10. html 空链接 href="#"与href="javascript:void(0)"的区别

    #包含了一个位置信息 默认的锚是#top 也就是网页的上端 而javascript:void(0) 仅仅表示一个死链接 这就是为什么有的时候页面很长浏览链接明明是#但跳动到了页首 而javascrip ...