最近由于开发工作的需要,项目引入了boost::statechart的状态机,它大量了引用了CRTP, 它的全称是Curiously Recurring Template Pattern,奇异递归模板模式,C++模板编程中很常用的一种用法。那么它神奇的地方到底在哪里呢,接下来就一一来揭开它神秘的面纱。
一、奇异递归模板模式的简介
奇异递归模板模式的基本思想要点是:派生类要作为基类的模板参数。它是C++模板编程中常用的手法。理解它之后,学习模板编程过程中也会事半功倍,而不会觉得云里雾里的。
二、奇异递归模板模式的基本格式
奇异递归模板模式的基本格式如下:JCrtpDerived继承JCrtpBase,并且JCrtpDerived作为基类JCrtpBase的模板参数。通过这样的方式,基类就可以使用子类的方法。并且不需要使用到虚函数,一定程度上减少程序的开销。
三、奇异递归模板模式的入门
从上面的给出的奇异递归模板模式的基本格式中可以看出,子类是作为基类的模板参数,但是如果传递给基类的模板参数不是基类的子类,那就会造成混乱错误。如下图所示,JCrtpDerived2子类继承了基类JCrtpBase,但是传递给基类的模板参数不是JCrtpDerived2。
那么如何解决上面的问题呢,可以将基类的默认构造函数设置为私有,并且模板参数T设置为基类的友元。通过这样的方式,基类的构造函数只能由模板参数T调用。当创建JCrtpDerived2子类对象的时候,会调用基类的构造函数,而这时候发现JCrtpDerived2不是基类的友元,那么就无法调用基类构造函数而出错。
调用运行JCrtpDerived2,就会出现错误
四、奇异递归模板模式的应用场景1、静态多态
奇异递归模板模式可以实现静态多态的效果,顾名思义,就是有多态的特性,但是不需要使用虚函数,是编译的时候确定,因此c++模板类,能够减少运行时的开销。接下来就来看看两个示例。
基类JCrtpBase实现函数Do,该函数内部对象通过static_cast转换为模版参数对象,模板参数对象再调用对应的实现函数,而模板参数对象由子类来实现。
调用运行的效果如下所示,从中可以看出,对象调用基类的函数,而基类函数实际上又去调用子类的函数DoSomething。基于这样的思想,我们可以将通用的逻辑放在基类Do中实现,而不同的放到对应的子类函数DoSomething实现。
这样需要注意的一点是,如果子类再被其他子类继承c++模板类,那么其他子类就不能按照上面的方式实现。具体可以看下示例:JCrtpSub子类再继承JCrtpDerived1。
调用运行的效果如下所示,JCrtpSub调用基类的函数Do,但是运行没有调用到JCrtpSub类自身的函数DoSomething。
上面的例子是子类调用基类函数,由基类再转换调用子类函数,效果类似于策略模式。下面将要说明的例子,更像多态特性,但是不需要虚函数。基类和子类都实现相同的函数DoSomething
然后实现模板方法,该方法入参为基类JCrtpBase的引用,内部调用基类函数DoSomething。
调用运行效果如下,向模板方法传递不同的子类,调用对应子类的函数。
2、boost::statechart状态机
Boost.Statechart大量使用了CRTP, 派生类必须作为第一个参数传递给所有基类模板,Boost.Statechart状态机后续考虑作为一个专题来研究讨论。
3、std::enable_shared_from_this特性
C++的特性enable_shared_from_this通常是用于在当你要返回一个被shared_ptr管理的对象。JObj继承enable_shared_from_this,并且JObj作为参数模板传递给enable_shared_from_this,这里就运用到了CRTP。
正确的调用方式,JObj是被shared_ptr管理,因此,如果要获取对象,那么JObj需要继承enable_shared_from_this。
五、总结
到这里,奇异递归模板模式已经基本讲解完成,我们首先介绍了它的基本格式,使用注意要点,然后重点讲解了它的应用场景,包括静态多态、boost::statechart状态机、std::enable_shared_from_this特性。理解了奇异递归模板模式,不但有利于模板编程的学习,而且对于以后应用的开发也是有好处的。
限时特惠:本站每日持续更新海量设计资源,一年会员只需29.9元,全站资源免费下载
站长微信:ziyuanshu688