关于CRTP(Curiously Recurring Template Prattern)的使用
2016-02-17 01:07
597 查看
在阅读frameworks/rs/cpp/util/RefBase.h之LightRefBase时,我记得《C++设计新思维》里对这种用法是有过介绍的,可是今天翻箱倒柜,怎么都找不到那本奇书了。当年所谓的前卫,今天已经遍地开花,赶紧再把CRTP给复习一下。
CRTP模式主要有两种使用场景:
一、Meyers Counting
CountedA类和CountedB类都具备了引用计数功能,这是从Counting基类派生下来的,但是引用计数具体的数值是每个子类各自维护,这主要是拜mCount的static属性所赐,Counting<CountedA>和Counting<CountedB>是不同的类,也就有不同的静态成员。
不过LightRefBase显然不是这种应用场景。
二、用编译时绑定替代运行时绑定,避免虚表的时空性能开销。
上面,Fuck()和doFuck()共同完成了虚函数的表演,但无需承担虚表的开销。乍一看LightRefBase也不是这种设计,因为它显然没有虚函数的桥段。
它唯一使用了模板参数的地方就是#11,可以断定此处就是该设计的初衷。如果用派生的方式,也可以完成这个任务:
可这样一来就要引入虚表了——有一个析构虚函数。为什么必须要有它呢?因为Base要用于派生。《Effective C++》第14条曰:总让base class拥有virtual destructor,因为经由基类指针删除子类对象时,基类如果没有虚析构函数,结果将是未定义。于是定义虚析构函数,于是引入虚表。LightRefBase对象的开销本来只有一个int32_t那么大,引入虚表,空间开销就double啦,大大得不划算。因此采用了CRTP模式,算是场景二的变种。
CRTP模式主要有两种使用场景:
一、Meyers Counting
template <typename T> class Counting { public: Counting() { mCount++; } ~Counting() { mCount--; } static int mCount; }; template <typename T> int Counting<T>::mCount = 0; class CountedA : public Counting<CountedA>{}; class CountedB : public Counting<CountedB>{};
CountedA类和CountedB类都具备了引用计数功能,这是从Counting基类派生下来的,但是引用计数具体的数值是每个子类各自维护,这主要是拜mCount的static属性所赐,Counting<CountedA>和Counting<CountedB>是不同的类,也就有不同的静态成员。
不过LightRefBase显然不是这种应用场景。
二、用编译时绑定替代运行时绑定,避免虚表的时空性能开销。
#include "stdafx.h" #include <stdio.h> template <typename T> class Fucker { public: void doFuck() { printf("Failed :(\n"); } void Fuck() { ((T*)this)->doFuck(); } }; class AFucker : public Fucker<AFucker> {}; class BFucker : public Fucker<BFucker> { public: void doFuck() { printf("Shuang :)\n"); } }; int main(int argc, char** argv) { Fucker<BFucker>* pFucker = new BFucker(); pFucker->Fuck(); return 0; }
上面,Fuck()和doFuck()共同完成了虚函数的表演,但无需承担虚表的开销。乍一看LightRefBase也不是这种设计,因为它显然没有虚函数的桥段。
template <class T> class LightRefBase { public: inline LightRefBase() : mCount(0) { } inline void incStrong(__attribute__((unused)) const void* id) const { __sync_fetch_and_add(&mCount, 1); } inline void decStrong(__attribute__((unused)) const void* id) const { if (__sync_fetch_and_sub(&mCount, 1) == 1) { delete static_cast<const T*>(this); } } //! DEBUGGING ONLY: Get current strong ref count. inline int32_t getStrongCount() const { return mCount; } typedef LightRefBase<T> basetype; protected: inline ~LightRefBase() { } private: friend class ReferenceMover; inline static void moveReferences(void*, void const*, size_t, const ReferenceConverterBase&) { } private: mutable volatile int32_t mCount; };
它唯一使用了模板参数的地方就是#11,可以断定此处就是该设计的初衷。如果用派生的方式,也可以完成这个任务:
class Base { public: Base() :mCount(0) {} int IncRef() { return ++mCount; } int DecRef() { if (--mCount <= 0) { delete this; } return mCount; } virtual ~Base() {} private: int mCount; }; class Derived : public Base {};
可这样一来就要引入虚表了——有一个析构虚函数。为什么必须要有它呢?因为Base要用于派生。《Effective C++》第14条曰:总让base class拥有virtual destructor,因为经由基类指针删除子类对象时,基类如果没有虚析构函数,结果将是未定义。于是定义虚析构函数,于是引入虚表。LightRefBase对象的开销本来只有一个int32_t那么大,引入虚表,空间开销就double啦,大大得不划算。因此采用了CRTP模式,算是场景二的变种。
相关文章推荐
- 剖析php脚本的超时机制
- PHP IDE推荐与评测 ZendStudio VS.PHP PhpStorm
- php脚本运行时的超时机制详解
- Atitit.跨平台预定义函数 魔术方法 魔术函数 钩子函数 api兼容性草案 v2 q216 java c# php js.docx
- Atitit.跨平台预定义函数 魔术方法 魔术函数 钩子函数 api兼容性草案 v2 q216 java c# php js.docx
- THINKPHP自动验证,自动完成,字段…
- PHP列表一次性读取字段信息
- THINKPHP钩子方法
- WEIPHP使用
- THINKPHP
- THINKPHP RBAC
- ThinkPHP去除url中的index.php
- PHP正则表达式
- IIS6切换PHP版本
- THINKPHP日常用到的基础知识
- PHPCMS一些常用调用方法
- phpcms推荐位分组
- PHPCMS URL规则(频道页(栏目页列表页)二级域名,内容页主域名)
- PHPCMS整合UCENTER后登陆问题
- thinkphp主从分离分布式处理