概述

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

与多态的区别

  • 多态是动态绑定(运行时绑定),CRTP是静态绑定(编译时绑定)
  • 在实现多态时,需要重写虚函数,因而这是运行时绑定的操作。
  • CRTP在编译期确定通过基类来得到派生类的行为,它通过派生类覆盖基类成员函数来实现静态绑定的。

例子1

说明:

  • 父类调用直接子类函数的方法:

    • 静态函数成员:this 指针不可见,而对于某一个实例化的继承层次来说,只有一个静态类,因此使用Derived::memberfun()实现
    • 非静态函数成员:调用非静态成员函数时,必须通过对象或者对象指针,因此将this指针进行静态转换。static_cast(this)
  • 函数成员的覆盖
    如果子类中有和父类相同名称的函数(不管是否静态),父类版本被屏蔽。
  • 其他
    通过子类的对象或者对象指针调用其成员函数时,总是优先在其定义中寻找可行函数,如果没有找到,就执行父类的实现版本

crtp.h

  1. #include<iostream>
  2. #include<stddef.h>
  3. using namespace std;
  4. template<class Derived>
  5. struct Base
  6. {
  7. void Interface()
  8. {
  9. cout <<"come from Interface"<<endl;
  10. // 转换为子类指针,编译期将绑定至子类方法
  11. static_cast<Derived*>(this)->Implementation();
  12. }
  13. static void StaticInterface()
  14. {
  15. // 编译期将绑定至子类方法
  16. cout <<"come from StaticInterface"<<endl;
  17. Derived::StaticImplementation();
  18. }
  19. void Implementation()
  20. {
  21. cout <<"Base Implementation"<<endl;
  22. return;
  23. }
  24. static void StaticImplementation()
  25. {
  26. cout << "Base StaticImplementation"<<endl;
  27. return;
  28. }
  29. };
  30. // The Curiously Recurring Template Pattern (CRTP)
  31. struct Derived1 : Base<Derived1>
  32. {
  33. static void StaticImplementation();
  34. };
  35. struct Derived2 : Base<Derived2>
  36. {
  37. void Implementation();
  38. };

crtp.cc

  1. void Derived1::StaticImplementation()
  2. {
  3. cout << "StaticImplementation from Derived1"<<endl;
  4. return;
  5. }
  6. void Derived2::Implementation()
  7. {
  8. cout <<"Implementation from Derived2"<<endl;
  9. return;
  10. }
  • test-crtp.cc
  1. int main()
  2. {
  3. cout << "***********************************" << endl;
  4. Derived1 derive1;
  5. Derived2 derive2;
  6. derive1.Implementation();
  7. derive1.StaticImplementation();
  8. derive2.Implementation();
  9. derive2.StaticImplementation();
  10. cout << "***********************************" << endl << endl;
  11. Base<Derived1> base_derive1;
  12. Base<Derived2> base_derive2;
  13. base_derive1.Implementation();
  14. base_derive1.StaticImplementation();
  15. base_derive2.Implementation();
  16. base_derive2.StaticImplementation();
  17. cout << "***********************************" << endl << endl;
  18. base_derive1.StaticInterface();
  19. base_derive1.Interface();
  20. base_derive2.StaticInterface();
  21. base_derive2.Interface();
  22. cout << "***********************************" << endl << endl;
  23. return 0;
  24. }

运行结果如下:

  1. ***********************************
  2. Base Implementation
  3. StaticImplementation from Derived1
  4. Implementation from Derived2
  5. Base StaticImplementation
  6. ***********************************
  7. Base Implementation
  8. Base StaticImplementation
  9. Base Implementation
  10. Base StaticImplementation
  11. ***********************************
  12. come from StaticInterface
  13. StaticImplementation from Derived1
  14. come from Interface
  15. Base Implementation
  16. come from StaticInterface
  17. Base StaticImplementation
  18. come from Interface
  19. Implementation from Derived2
  20. ***********************************

总结

  • 第一组结果说明:

    • 如果子类中有和父类相同名称的函数(不管是否静态),父类版本被屏蔽。
    • 如果子类中没有找到成员函数,就执行父类的实现版本
  • 第二组结果说明:
    • 通过Base<子类>对象调用成员函数时,就和通过Base对象调用成员函数一样的效果,不管实例化模板使用的模板实参是什么
  • 第三组结果说明:
    • 通过在Base<子类>接口函数(InterfaceStaticInterface)调用其他成员函数,可以通过使用不同的模板实参来实例化模板实现不同的接口调用效果。

应用1:实现计数

统计每个类的对象个数

  1. template <typename T>
  2. struct counter
  3. {
  4. counter(){ objects_created++;objects_alive++;}
  5. virtual ~counter(){--objects_alive;}
  6. static int objects_created;
  7. static int objects_alive;
  8. };
  9. // 类外初始化
  10. template <typename T> int counter<T>::objects_created( 0 );
  11. template <typename T> int counter<T>::objects_alive( 0 );
  12. class X : counter<X>{// ...};
  13. class Y : counter<Y>{ // ...};
  14. //X和Y类都有自己的计数

应用2:实现对象的引用计数

  • ns3中simple-ref-count.h

此处m_count是对象的成员变量

  1. template <typename T, typename PARENT = empty, typename DELETER = DefaultDeleter<T> >
  2. class SimpleRefCount : public PARENT
  3. {
  4. public:
  5. SimpleRefCount (): m_count (1){}
  6. inline void Ref (void) const
  7. {
  8. m_count++;
  9. }
  10. inline void Unref (void) const
  11. {
  12. m_count--;
  13. if (m_count == 0)
  14. {
  15. DELETER::Delete (static_cast<T*> (const_cast<SimpleRefCount *> (this)));
  16. }
  17. }
  18. mutable uint32_t m_count;
  19. };
  • ns3中某一个需要计数的类
  1. class Object : public SimpleRefCount<Object, ObjectBase, ObjectDeleter>
  2. {
  3. .....
  4. }

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. MySQL半同步复制

    从MySQL5.5开始,MySQL以插件的形式支持半同步复制.如何理解半同步呢?首先我们来看看异步,全同步的概念 异步复制(Asynchronous replication) MySQL默认的复制即是 ...

  2. PHP环境配置-从Apache官网下载windows版apache服务器

    由于个人有强迫倾向,下载软件都喜欢从官网下载,摸索了好久终于摸清楚怎么从Apache官网下载windows安装版的Apache服务器了,现在分享给大家. 进入apache服务器官网http://htt ...

  3. [TSM]在调度计划的时候出现 “ANS1125E Unmatched Quotes: 'string' ”错误的替代解决办法

    环境: TSMserver:TSM 6.2.3 for Windows Server 2008 R2 TSMclient: TSM 5.5.0 for CentOS 遇到的故障: ANS1125E U ...

  4. 使用RMAN创建复制数据库

    我的实验环境: - 源数据库A机: RHEL6.4 + Oracle 11.2.0.4 IP地址:192.168.99.159 db_name=oradb 数据库已正常运行 - 复制数据库B机: RH ...

  5. CentOS 7.2 yum方式安装MySQL 5.7

    CentOS 7之后的版本yum的默认源中使用MariaDB替代原先MySQL,因此安装方式较为以往有一些改变: 下载mysql的源 wget http://dev.mysql.com/get/mys ...

  6. SAX解析技术

    SAX,全称Simple API for XML,既是指一种接口,也是指一个软件包.SAX工作原理简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束.元素(element)开始 ...

  7. Node.js、express、mongodb 入门(基于easyui datagrid增删改查)

    前言 从在本机(win8.1)环境安装相关环境到做完这个demo大概不到两周时间,刚开始只是在本机安装环境并没有敲个Demo,从周末开始断断续续的想写一个,按照惯性思维就写一个增删改查吧,一方面是体验 ...

  8. 分享在winform下实现左右布局多窗口界面-续篇

    之前的这篇文章<分享在winform下实现左右布局多窗口界面>已经实现了左右布局多窗口界面,今天本来是研究基于winform的插件编程,没想到顺便又找到了另一种实现方案,这种实现方案更简单 ...

  9. java对email邮箱的真实、有效性验证

    三种验证邮箱有效性的方式: 方式1: public static boolean checkEmail(String email) {     if (!email.matches("[\\ ...

  10. Lind.DDD敏捷领域驱动框架~Lind.DDD各层介绍

    回到目录 Lind.DDD项目主要面向敏捷,快速开发,领域驱动等,对于它的分层也是能合并的合并,比之前大叔的框架分层更粗糙一些,或者说更大胆一些,在开发人员使用上,可能会感觉更方便了,更益使用了,这就 ...