您的位置:首页 > 编程语言 > PHP开发

关于CRTP(Curiously Recurring Template Prattern)的使用

2016-02-17 01:07 597 查看
在阅读frameworks/rs/cpp/util/RefBase.h之LightRefBase时,我记得《C++设计新思维》里对这种用法是有过介绍的,可是今天翻箱倒柜,怎么都找不到那本奇书了。当年所谓的前卫,今天已经遍地开花,赶紧再把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模式,算是场景二的变种。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: