🎯 场景 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 指针 |
| 扩展性 | 编译时确定 | 运行时可扩展 |
| 插件支持 | ❌ 不支持 | ✅ 支持 |
| 运行时决策 | ❌ 不支持 | ✅ 支持 |
| 类型安全性 | 更强 | 强 |
| 编译复杂度 | 高 | 低 |