【一分钟学C++】CRTP

在这里插入图片描述

竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生~
公众号: C++学习与探索  |  个人主页: rainInSunny  |  个人专栏: Learn OpenGL In Qt

概述

  CRTP(Curiously Recurring Template Pattern)是一种 C++ 编程技巧,使用模板类和继承的组合来实现静态多态。该模式的关键思想是:在模板类的定义中,模板参数是当前类自身(通常是派生类)。这个技巧通常用于实现编译时多态,优化性能。翻译成代码,一般是这种形式:

// 先定义一个模板类作为基类
template <typename T>
class Base
{
    ...
};
 // 定义一个派生类,这个类继承以自身作为参数的基类
class Derived : public Base<Derived>
{
    ...
};

代码分析

  假设当前需要设计一个 Info 类中包含 getName() 接口,有不同的子类对该接口进行实现,很容易想到的方法是利用 C++ 的多态机制,实现大概是这样:

struct IInfo
{
    virtual std::string getClassName() = 0;
};

class A : public IInfo
{
    std::string getClassName() override
    {
        return "A";
    }
};

class B : public IInfo
{
    std::string getClassName() override
    {
        return "B";
    }
};

  这样是传统动态多态的实现方法,在运行时通过查询虚函数表,找到实际调用接口,返回正确的类名。在看看如果用 CRTP 的形式如何实现:

template <typename T>
class Info
{
    std::string getClassName()
    {
        return static_cast<T *>(this)->getClassNameImpl();
    }
};

class A : public Info<A>
{
    std::string getClassNameImpl()
    {
        return "A";
    }
};

class B : public Info<B>
{
    std::string getClassNameImpl()
    {
        return "B";
    }
};

  这里使用了 static_cast 进行类型转换,根据 CRTP 的定义,在 Info 的派生类中调用 getClassName 接口,并且 T 就是这里的派生类,这里的 static_cast 转换一定是合法的,因为这里的 this 就是派生类型 T。

代码测试

  测试代码:

int main()
{
    IInfo *pA = new A();
    IInfo *pB = new B();
    std::cout << pA->getClassName() << std::endl;
    std::cout << pB->getClassName() << std::endl;

    C classC;
    D classD;
    std::cout << classC.getClassName() << std::endl;
    std::cout << classD.getClassName() << std::endl;
    return 0;
}

  测试结果如图,动态多态和静态多态都能够实现输出正确的类名。
在这里插入图片描述

对比

静态多态和动态多态对比
特性CRTP(静态多态)动态多态
性能高效,无运行时开销有虚函数表查找开销,性能略低
灵活性受限,类型在编译时固定灵活,类型可以在运行时动态选择
类型安全性高,编译时检查低,存在类型转换失败风险
编译期 vs 运行期完全在编译时依赖运行时
耦合性较高,A 模块使用 B 模块中的 CRTP 实现,涉及到的符号都得对 A 模块可见较低,A 模块使用 B 模块中的接口类,接口实际实现的类不需要对 A 模块暴露
可读性很差,涉及到模版,还存在代码体积膨胀问题较差

关注公众号:C++学习与探索,有惊喜哦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值