C++(不适合使用 CRTP情况)

🎯 场景 1:运行时决策

什么是"运行时决策"?

程序在执行期间才决定调用哪个具体类型的函数,而不是在编译时确定。

❌ CRTP 无法处理运行时决策

cpp 复制代码
#include <iostream>
#include <memory>

// ❌ CRTP 无法实现运行时决策
template<typename Derived>
class ShapeBase {
public:
    double area() const {
        return static_cast<const Derived*>(this)->area_impl();  // 编译时绑定
    }
};

class Circle : public ShapeBase<Circle> {
public:
    double area_impl() const { return 3.14 * 5 * 5; }
};

class Square : public ShapeBase<Square> {
public:
    double area_impl() const { return 10 * 10; }
};

int main() {
    int choice;
    std::cin >> choice;  // 用户输入决定形状

    // ❌ 编译错误!无法在运行时决定类型
    // if (choice == 1) {
    //     ShapeBase<?> shape = Circle{};  // 无法确定 ? 是什么类型
    // } else {
    //     ShapeBase<?> shape = Square{};  // 无法确定 ? 是什么类型
    // }

    return 0;
}

✅ 动态多态可以处理运行时决策

cpp 复制代码
#include <iostream>

// ✅ 虚函数支持运行时决策
class Shape {
public:
    virtual double area() const = 0;
    virtual ~Shape() = default;
};

class Circle : public Shape {
public:
    double area() const override { return 3.14 * 5 * 5; }
};

class Square : public Shape {
public:
    double area() const override { return 10 * 10; }
};

int main() {
    int choice;
    std::cin >> choice;

    std::unique_ptr<Shape> shape;  // 统一接口

    if (choice == 1) {
        shape = std::make_unique<Circle>();  // 运行时创建
    } else {
        shape = std::make_unique<Square>();  // 运行时创建
    }

    std::cout << "Area: " << shape->area() << std::endl;  // 运行时决定调用哪个函数
    return 0;
}

🎯 场景 2:类型层次不确定

什么是"类型层次不确定"?

程序不知道会有哪些派生类,这些类可能来自外部、用户定义或后续扩展。

❌ CRTP 无法处理未知类型

cpp 复制代码
// 假设你是一个图形库作者
template<typename Derived>
class ShapeBase {
public:
    void draw() const {
        static_cast<const Derived*>(this)->draw_impl();
    }
};

// ❌ 你的库只能提供有限的形状
class Circle : public ShapeBase<Circle> {
public:
    void draw_impl() const { /* ... */ }
};

class Rectangle : public ShapeBase<Rectangle> {
public:
    void draw_impl() const { /* ... */ }
};

// ❌ 如果用户想添加新形状,必须修改你的库
// class UserTriangle : public ShapeBase<UserTriangle> {  // ❌ 用户无法扩展你的库
// public:
//     void draw_impl() const { /* ... */ }
// };

✅ 动态多态允许任意扩展

cpp 复制代码
// 图形库只需要定义接口
class Shape {
public:
    virtual void draw() const = 0;
    virtual ~Shape() = default;
};

// 用户可以在任何时候添加新形状
class UserTriangle : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing user-defined triangle" << std::endl;
    }
};

// 库可以处理任意的 Shape 派生类
void renderShapes(const std::vector<std::unique_ptr<Shape>>& shapes) {
    for (const auto& shape : shapes) {
        shape->draw();  // 无论什么派生类都能正确调用
    }
}

🎯 场景 3:需要动态加载插件

什么是"动态加载插件"?

程序运行时从外部文件(如 DLL、SO 文件)加载代码并使用其中的类。

❌ CRTP 无法处理动态加载

cpp 复制代码
// ❌ 编译时就必须知道所有类型
// Plugin.dll 中定义:
class PluginShape : public ShapeBase<PluginShape> {  // ❌ 无法在编译时知道这个类型
public:
    void draw_impl() const { /* ... */ }
};

// 主程序无法提前知道 PluginShape
// ShapeBase<?> shape = load_plugin_shape();  // ❌ 编译错误

✅ 动态多态支持插件系统

cpp 复制代码
// 插件接口(定义在头文件中)
class IPluginShape {
public:
    virtual void draw() const = 0;
    virtual ~IPluginShape() = default;
};

// Plugin.dll 中实现
class PluginShape : public IPluginShape {
public:
    void draw() const override {
        std::cout << "Drawing plugin shape" << std::endl;
    }
};

// 导出创建函数(供主程序调用)
extern "C" IPluginShape* create_shape() {
    return new PluginShape();
}

extern "C" void destroy_shape(IPluginShape* shape) {
    delete shape;
}

// 主程序
int main() {
    // 动态加载插件
    void* handle = dlopen("./plugin.so", RTLD_LAZY);
    IPluginShape* (*create_func)() = (IPluginShape*(*)())dlsym(handle, "create_shape");
    
    IPluginShape* plugin_shape = create_func();  // 运行时创建插件对象
    plugin_shape->draw();  // 虚函数调用,正确执行插件实现
    
    return 0;
}

🧩 实际案例:游戏引擎中的渲染器

cpp 复制代码
// ❌ CRTP 无法实现灵活的渲染器插件
template<typename RendererType>
class RenderSystem {
public:
    void render() {
        static_cast<RendererType*>(this)->render_impl();  // 编译时绑定
    }
};

// OpenGLRenderer, DirectXRenderer, VulkanRenderer... 都需要在编译时确定

// ✅ 动态多态实现灵活的渲染器插件
class IRenderer {
public:
    virtual void render() = 0;
    virtual ~IRenderer() = default;
};

class OpenGLRenderer : public IRenderer {
public:
    void render() override { /* OpenGL 实现 */ }
};

class DirectXRenderer : public IRenderer {
public:
    void render() override { /* DirectX 实现 */ }
};

// 游戏启动时根据配置决定使用哪个渲染器
std::unique_ptr<IRenderer> renderer;
if (config.use_opengl) {
    renderer = std::make_unique<OpenGLRenderer>();
} else if (config.use_directx) {
    renderer = std::make_unique<DirectXRenderer>();
}
renderer->render();  // 运行时决定调用哪个实现
场景 推荐方案 原因
🚀 高性能计算库 CRTP 编译时优化,零运行时开销
🎮 游戏引擎插件 虚函数 动态加载,运行时扩展
📦 配置驱动的应用 虚函数 根据配置决定实现
🧮 数学表达式模板 CRTP 编译时生成最优代码
🔌 第三方集成 虚函数 接口开放,便于扩展
特性 CRTP 动态多态
绑定时机 编译时 运行时
性能 更快(内联) 稍慢(vtable)
内存开销 无 每对象 +1 指针
扩展性 编译时确定 运行时可扩展
插件支持 ❌ 不支持 ✅ 支持
运行时决策 ❌ 不支持 ✅ 支持
类型安全性 更强 强
编译复杂度 高 低
相关推荐
java资料站3 小时前
十一、评估测试
开发语言·人工智能·python
左手の明天3 小时前
SLIP 协议封装与解析详解:原理、C 代码与实战案例
linux·c语言·开发语言·c++
海上小飞龙4 小时前
【KMP算法-下篇】同一道题:Java 库函数 2600 微秒,手写 KMP 16 微秒
java·开发语言·算法
青少儿编程课堂4 小时前
后缀自动机解析:本质不同子串与最长重复片段统计
c++·python·算法·bfs·信息学竞赛
朝朝辞暮i4 小时前
C++ 第 12 课:局部变量、作用域、变量生命周期
开发语言·c++·算法
狗凯之家源码网4 小时前
淘客口令落地页生成器 PHP 版深度评测
开发语言·php
画绛集美术4 小时前
本地化部署LLM用于美术史问答:一间美术教室的实践记录
开发语言·php
八解毒剂5 小时前
【计组】中央处理器CPU
java·开发语言·计算机组成原理
FakeOccupational5 小时前
【电路笔记 信号】DBPSK 波形查找表+脉冲成形(升余弦+根升余弦滤波)
开发语言·笔记
打工仔折腾 AI6 小时前
工业场景下时序库与实时计算一体化架构选型实践对比
java·开发语言·后端·python·性能优化·架构·ai agent 实战