栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

初探C++ CRTP(奇异的递归模板模式)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

初探C++ CRTP(奇异的递归模板模式)

文章目录
    • 1.什么是CRTP?
    • 2.CRTP的优点
    • 3.静态多态(Static polymorphism)
    • 2.轻松地实现各个子类实例创建和析构独立的计数
    • 3.多态链(Polymorphic chaining)
    • 4.多态的拷贝构造(Polymorphic copy construction)。

1.什么是CRTP?

简单来说,CRTP有两大特性:

  • 继承自模板类。
  • 派生类将自身作为参数传给模板类。
// 我们先定义一个模板类作为基类
template 
class Base
{
    ...
};
 // 定义一个派生类,这个类继承以自身作为参数的基类
class Derived : public Base
{
    ...
};

问题来了,为什么要这样做呢?

  • 这样做的目的其实很明确,从基类对象的角度来看,派生类对象其实就是本身,这样的话只需要用一个static_cast就可以把基类转化成派生类,从而实现基类对象对派生对象的访问。
  • eg:
template 
class Base
{
public:
    void doSomething()
    {
        T& derived = static_cast(*this);
    }
};

class Derived : public Base
{
public:
    void doSomething()
    {
         std::cout << " Derived class " << std::endl;
    }  
};
  • 这里将基类转换成派生类用的是static_cast静态绑定,而普通基类转派生类用的是dynamic_cast动态绑定。
    动态绑定的目的是为了确保你所转化的派生类是正确的,而对于CRTP来说,基类是继承于模板类的参数,也就是派生类本身。 这也正是CRTP这种设计的目的。
2.CRTP的优点

多态是个很好的特性,但是动态绑定比较慢,因为要查虚函数表。

  • 而使用 CRTP,完全消除了动态绑定,降低了继承带来的虚函数表查询开销
3.静态多态(Static polymorphism)

eg:

  • 以Base::interface() 为例,在Derived : Base中,Base是先于Derived而存在的,所以当Base::interface()被声明时,编译器并不知道Derived的存在的,所以此时 Base::interface() 并不会被实例化。
  • 只有当Base::interface()被调用时,才会被实例化,而此时编译器也已经知道了 Derived::implementation()的声明了。
  • 这里等同于通过查询虚函数动态绑定以达到多态的效果,但省略了动态绑定虚函数查询的时间。
template  
struct Base
{
    void interface()
    {
        // ...
        static_cast(this)->implementation();
        // ...
    }

    static void static_func()
    {
        // ...
        T::static_sub_func();
        // ...
    }
};

struct Derived : Base
{
    void implementation();

    static void static_sub_func();
};
2.轻松地实现各个子类实例创建和析构独立的计数

eg:

  • 每次X或者Y实例化时,counter或者 counter就会被调用,对应的就会增加对创建对象的计数。
  • 同样,每次X或者Y析构后,对应的减少objects_alive。这里最重要的是实现了对不同子类单独的计数。
template 
struct counter
{
    static int objects_created;
    static int objects_alive;

    counter()
    {
        ++objects_created;
        ++objects_alive;
    }
    
    counter(const counter&)
    {
        ++objects_created;
        ++objects_alive;
    }
protected:
    ~counter() // objects should never be removed through pointers of this type
    {
        --objects_alive;
    }
};
template  int counter::objects_created( 0 );
template  int counter::objects_alive( 0 );

class X : counter
{
    // ...
};

class Y : counter
{
    // ...
};
3.多态链(Polymorphic chaining)

eg:

  • printer定义了打印的方法,CoutPrinter是Printer的子类,并且添加了一个设置打印颜色的方法。
class Printer
{
public:
    Printer(ostream& pstream) : m_stream(pstream) {}
 
    template 
    Printer& print(T&& t) { m_stream << t; return *this; }
 
    template 
    Printer& println(T&& t) { m_stream << t << endl; return *this; }
private:
    ostream& m_stream;
};

class CoutPrinter : public Printer
{
public:
    CoutPrinter() : Printer(cout) {}

    CoutPrinter& SetConsoleColor(Color c)
    {
        // ...
        return *this;
    }
};
  • 接下来我们看看下面这行代码
    前半段CoutPrinter().print("Hello ")调用的是Printer实例,后面接着SetConsoleColor(Color.red)实际上又需要调用CoutPrinter实例,这样编译器就会报错。
CoutPrinter().print("Hello ").SetConsoleColor(Color.red).println("Printer!");
  • 而CRTP就可以很好的解决这个问题,代码如下:
// Base class
template 
class Printer
{
public:
    Printer(ostream& pstream) : m_stream(pstream) {}
 
    template 
    ConcretePrinter& print(T&& t)
    {
        m_stream << t;
        return static_cast(*this);
    }
 
    template 
    ConcretePrinter& println(T&& t)
    {
        m_stream << t << endl;
        return static_cast(*this);
    }
private:
    ostream& m_stream;
};
 
// Derived class
class CoutPrinter : public Printer
{
public:
    CoutPrinter() : Printer(cout) {}
 
    CoutPrinter& SetConsoleColor(Color c)
    {
        // ...
        return *this;
    }
};
 
// usage
CoutPrinter().print("Hello ").SetConsoleColor(Color.red).println("Printer!");
4.多态的拷贝构造(Polymorphic copy construction)。

当我们用到多态时,经常会需要通过基类的指针来复制子对象。

  • 通常我们可以通过在基类里面构造一个clone()虚函数,然后在每个子类里面定义这个clone()函数。使用CRTP可以让我们避免反复地在子类中去定义clone()函数。
  • 看到virtual,就说明这里还是需要运行时的多态,因为实例化后的子类类型无法在编译期进行确定
  • CRTP实现不了动态多态
// Base class has a pure virtual function for cloning
class AbstractShape {
public:
    virtual ~AbstractShape () = default;
    virtual std::unique_ptr clone() const = 0;
};

// This CRTP class implements clone() for Derived
template 
class Shape : public AbstractShape {
public:
    std::unique_ptr clone() const override {
        return std::make_unique(static_cast(*this));
    }

protected:
   // We make clear Shape class needs to be inherited
   Shape() = default;
   Shape(const Shape&) = default;
};

// Every derived class inherits from CRTP class instead of abstract class
class Square : public Shape{};

class Circle : public Shape{};
  • 参考:c++ CRTP(奇异的递归模板模式)介绍,c++ 中CRTP的用途
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1006283.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号