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 指针
扩展性 编译时确定 运行时可扩展
插件支持 ❌ 不支持 ✅ 支持
运行时决策 ❌ 不支持 ✅ 支持
类型安全性 更强
编译复杂度
相关推荐
计算机安禾20 分钟前
【c++面向对象编程】第25篇:仿函数(函数对象):重载operator()
开发语言·c++·算法
Rust语言中文社区20 分钟前
【Rust日报】2026-05-14 Pyrefly v1.0 正式发布:快速的 Python 类型检查器和语言服务器
开发语言·后端·python·rust
kkeeper~40 分钟前
0基础C语言积跬步之深入理解指针(4)
c语言·开发语言
周末也要写八哥1 小时前
在C++中使用预定义宏
开发语言·c++·算法
Data_Journal1 小时前
使用Python lxml轻松进行网络爬取
开发语言·php
xcLeigh1 小时前
IoTDB JDBC 完整使用教程:连接、查询、批处理与字符集配置
开发语言·数据库·qt·iotdb·查询·批处理·连接
学会870上岸华师1 小时前
C 语言程序设计——第一章课后编程题
c语言·开发语言·学习·算法
小小编程路1 小时前
新手快速学 Python 极简速成指南
开发语言·c++·python
rabbit_pro2 小时前
SpringBoot3集成Langchain4j使用Ollama
java·开发语言
宏笋2 小时前
C++ 约束模板参数Concepts详解
c++