当前位置: 首页 > news >正文

CRTP在项目优化中的使用

CRTP基本概念

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

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

CRTP的基本原理

        基类模板利用了其成员函数体(即成员函数的实现)在声明之后很久都不会被实例化(实际上只有被调用的模板类的成员函数才会被实例化),并利用了派生类的成员函数(通过类型转化)。

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

class Analimal
{
    virtual std::string getCharacters() = 0;
};

class Cat: public Analimal
{
    std::string getCharacters() override
    {
        return "Cat is a small animal...";
    }
};

class Dog : public Analimal
{
    std::string getCharacters() override
    {
        return "Dog is a loaty anmial...";
    }
};

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

CRTP 的形式如何实现:

template <typename T>
class Analimal
{
    virtual std::string getCharacters()
   {
     // 强制转换为子类,调用子类的characters()
      return static_cast<T *>(this)->characters();
   }
};

class Cat: public Analimal<Cat>
{
    std::string characters() override
    {
        return "Cat is a small animal...";
    }
};

class Dog : public Analimal<Cat>
{
    std::string characters() override
    {
        return "Dog is a loaty anmial...";
    }
};

        基类在编译时就可以知道派生类的信息!因此,以前是虚函数调用,现在是在编译期就将模板与正确的函数进行绑定。(通过继承实现虚函数的功能,又没有了虚函数调用产生的开销),这种“虚调用”不会产生过多的开销。编译器在编译时就一直跟踪调用方法,甚至会内联它。 

CRTP与传统动态多态对比

特性

CRTP(静态多态)

动态多态

性能

高效,无运行时开销(内联优化可能性大)

有虚函数表查找开销,性能略低

灵活性

受限,类型在编译时固定

灵活,类型可以在运行时动态选择

类型安全性

高,编译时检查

低,存在类型转换失败风险

编译期 vs 运行期

完全在编译时

依赖运行时

耦合性

较高,A 模块使用 B 模块中的 CRTP 实现,涉及到的符号都得对 A 模块可见

较低,A 模块使用 B 模块中的接口类,接口实际实现的类不需要对 A 模块暴露

可读性

很差,涉及到模版,还存在代码体积膨胀问题

较差

使用场景

静态多态

以以上Analimal的代码为例,实际使用时。

template <typename T>
void getCharacterDsc(T& base){
    base.getCharacters();
}

int main(){
  Dog d;
  Cat c;
  getCharacterDsc(d);
  getCharacterDsc(c);
  return 0;
}

        定义了一个函数getCharacterDsc(),在其函数体内调用getCharacters()函数。如果类型为Dog和Cat,则会调用这俩类型对应的characters()函数。即不使用virtual,也实现了多态功能,其二者的区别是:virtual是运行时多态,而CRTP则是在编译期就对模板进行了实例化,所以属于静态多态。 

代码复用

        现在需要实现一个功能,根据对象的具体类型,输出其类型名称。传统的动态多态写法如下,代码比较冗余。

#include <iostream>
#include <typeinfo>

class Base {
 public:
  virtual void PrintType() const {
    std::cout << typeid(*this).name() << std::endl;
  }
};

class Derived : public Base {
 public:
  virtual void PrintType() const {
    std::cout << typeid(*this).name() << std::endl;
  }
};
class Derived1 : public Base {
 public:
  virtual void PrintType() const {
    std::cout << typeid(*this).name() << std::endl;
  }
};

void PrintType(const Base& base) {
  base.PrintType();
}

使用CRTP则精简如下:

template<typename T>
class Base {
 public:
  void PrintType() {
    T &t = static_cast<T&>(*this);
    std::cout << typeid(t).name() << std::endl;
  }
};

class Derived : public Base<Derived> {};
class Derived1 : public Base<Derived1> {};

template<typename T>
void PrintType(T base) {
  base.PrintType();
}

 

http://www.dtcms.com/a/26291.html

相关文章:

  • Compose常用UI组件
  • WIN系统服务器如何修改远程端口?
  • Spring Bean 生命周期
  • 14 命令(Command)模式
  • 【STM32H743IIT6】正点原子阿波罗TFTLCD移植
  • C# ConcurrentQueue 使用详解
  • 14-二叉树最小深度-广度优先(BFS)
  • 帆软报表FineReport入门:简单报表制作[扩展|左父格|上父格]
  • Coze插件之基于IDE创建插件
  • 八股文-C++语言部分
  • 意图识别概述
  • 剑指 Offer II 024. 反转链表
  • 【Day44 LeetCode】图论问题 Ⅱ
  • javaSE学习笔记23-线程(thread)-总结
  • 伪类选择器
  • 修改项目的一些前端记录(自用)
  • JavaScript中判断元素是否在可视区域内
  • linux有名管道的文件描述符3和4
  • 个人简历html网页模板,科技感炫酷html简历模板
  • DeepSeek API调用 Python
  • Hive中的分区和桶的概念及其作用
  • 网络工程师 (47)QOS
  • 小怿学习日记(七) | Unreal引擎灯光架构
  • 【wrk】wrk 压测工具入门
  • DeepSeek与ChatGPT的对比分析
  • K8s 之端口暴露(The Port of K8s is Exposed)
  • Jmeter连接数据库、逻辑控制器、定时器
  • SOME/IP--协议英文原文讲解8
  • 《魔女的夜宴》无广版手游安卓苹果免费下载直装版
  • 红蓝对抗之常见网络安全事件研判、了解网络安全设备、Webshell入侵检测