zoukankan      html  css  js  c++  java
  • 关于CRTP(Curiously Recurring Template Prattern)的使用

    在阅读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 :(
    "); }
        void Fuck() { ((T*)this)->doFuck(); }
    };
    
    class AFucker : public Fucker<AFucker>
    {};
    
    class BFucker : public Fucker<BFucker>
    {
    public:
        void doFuck() { printf("Shuang :)
    "); }
    };
    
    int main(int argc, char** argv)
    {
        Fucker<BFucker>* pFucker = new BFucker();
        pFucker->Fuck();
        return 0;
    }

    上面,Fuck()和doFuck()共同完成了虚函数的表演,但无需承担虚表的开销。乍一看LightRefBase也不是这种设计,因为它显然没有虚函数的桥段。

     1 template <class T>
     2 class LightRefBase
     3 {
     4 public:
     5     inline LightRefBase() : mCount(0) { }
     6     inline void incStrong(__attribute__((unused)) const void* id) const {
     7         __sync_fetch_and_add(&mCount, 1);
     8     }
     9     inline void decStrong(__attribute__((unused)) const void* id) const {
    10         if (__sync_fetch_and_sub(&mCount, 1) == 1) {
    11             delete static_cast<const T*>(this);
    12         }
    13     }
    14     //! DEBUGGING ONLY: Get current strong ref count.
    15     inline int32_t getStrongCount() const {
    16         return mCount;
    17     }
    18 
    19     typedef LightRefBase<T> basetype;
    20 
    21 protected:
    22     inline ~LightRefBase() { }
    23 
    24 private:
    25     friend class ReferenceMover;
    26     inline static void moveReferences(void*, void const*, size_t,
    27             const ReferenceConverterBase&) { }
    28 
    29 private:
    30     mutable volatile int32_t mCount;
    31 };

    它唯一使用了模板参数的地方就是#11,可以断定此处就是该设计的初衷。如果用派生的方式,也可以完成这个任务:

     1 class Base
     2 {
     3 public:
     4     Base() :mCount(0) {}
     5     int IncRef() { return ++mCount; }
     6     int DecRef() {
     7         if (--mCount <= 0) {
     8             delete this;
     9         }
    10         return mCount;
    11     }
    12     virtual ~Base() {}
    13 private:
    14     int mCount;
    15 };
    16 
    17 class Derived : public Base
    18 {};

    可这样一来就要引入虚表了——有一个析构虚函数。为什么必须要有它呢?因为Base要用于派生。《Effective C++》第14条曰:总让base class拥有virtual destructor,因为经由基类指针删除子类对象时,基类如果没有虚析构函数,结果将是未定义。于是定义虚析构函数,于是引入虚表。LightRefBase对象的开销本来只有一个int32_t那么大,引入虚表,空间开销就double啦,大大得不划算。因此采用了CRTP模式,算是场景二的变种。

  • 相关阅读:
    如何让两个线程同时运行
    测试人员绩效评价方法
    转载:浅谈实施软件测试风险分析
    项目测试流程总结
    HttpClient使用小结
    浅谈分布式事务原理及其应用场景
    (.NET高级课程笔记)Lambd、Linq总结
    (.NET高级课程笔记)反射总结
    (.NET高级课程笔记)泛型总结
    Lambda表达式详解(例子详解)(转自:http://blog.csdn.net/damon316/article/details/51734661)
  • 原文地址:https://www.cnblogs.com/palance/p/5194164.html
Copyright © 2011-2022 走看看