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 指针
扩展性 编译时确定 运行时可扩展
插件支持 ❌ 不支持 ✅ 支持
运行时决策 ❌ 不支持 ✅ 支持
类型安全性 更强
编译复杂度
相关推荐
zhangyueping83851 小时前
大模型学习笔记-调用模型和基础使用
笔记·学习
宁静致远20212 小时前
ARM 架构 Ubuntu 20.04 / 22.04 触摸屏设备
linux·c++·ubuntu
栗少2 小时前
Python 入门教程(面向有 Java 经验的开发者)
java·开发语言·python
草莓熊Lotso2 小时前
Linux C++ 高并发编程:从原理到手撕,线程池全链路深度解析
linux·运维·服务器·开发语言·数据库·c++·mysql
小王师傅662 小时前
【Java结构化梳理】泛型-上
java·开发语言
wuxuanok2 小时前
Maven 编译报错:java.lang.NoSuchFieldError: JCImport 问题总结
java·开发语言·maven
Naiva2 小时前
《Fundamentals of Power Electronics 》章节1 Introduction
笔记
光电笑映2 小时前
Linux C/C++ 开发工具(下):make/Makefile、进度条小程序与 gdb 调试器
linux·c语言·c++