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

C++ CRTP 替代虚函数

基本原理:

CRTP(Curiously Recurring Template Pattern)是一种 C++ 编程设计模式,类似于 RAII、SFINAE、这些东西。

核心思想只有一个东西:

即派生类继承以自身为模板参数的基类模板,这样子呢,在 C++ 编译替换期间时,它可以知道模版类型信息的,所以,可以调用目标的成员函数。

不带虚函数(用途一):

#include <iostream> #include <vector> #include <cmath> #include <memory> #include <iomanip> #include <variant> // CRTP 基类模板,不继承任何虚基类,完全静态多态 template <typename Derived> class Shape { public: double area() const { return static_cast<const Derived*>(this)->calculateArea(); } double perimeter() const { return static_cast<const Derived*>(this)->calculatePerimeter(); } void printInfo() const { std::cout << std::fixed << std::setprecision(2) << "Area: " << area() << ", Perimeter: " << perimeter() << "\n"; } }; // 圆形 class Circle : public Shape<Circle> { double radius; public: Circle(double r) : radius(r) {} double calculateArea() const { return M_PI * radius * radius; } double calculatePerimeter() const { return 2 * M_PI * radius; } }; // 矩形 class Rectangle : public Shape<Rectangle> { double width, height; public: Rectangle(double w, double h) : width(w), height(h) {} double calculateArea() const { return width * height; } double calculatePerimeter() const { return 2 * (width + height); } }; // 三角形 class Triangle : public Shape<Triangle> { double a, b, c; public: Triangle(double s1, double s2, double s3) : a(s1), b(s2), c(s3) {} double calculateArea() const { double s = (a + b + c) / 2; return std::sqrt(s * (s - a) * (s - b) * (s - c)); } double calculatePerimeter() const { return a + b + c; } }; // 使用 std::variant 存储不同类型的形状,实现类型安全的统一处理 using ShapeVariant = std::variant<Circle, Rectangle, Triangle>; // Visitor 用于调用 printInfo struct PrintVisitor { template<typename T> void operator()(const T& shape) const { shape.printInfo(); } }; int main() { // 创建形状对象并存储在 std::variant 向量中 std::vector<ShapeVariant> shapes; shapes.emplace_back(Circle(5.0)); shapes.emplace_back(Rectangle(4.0, 6.0)); shapes.emplace_back(Triangle(3.0, 4.0, 5.0)); std::cout << "使用 std::variant 和 Visitor 统一处理(无虚函数调用):\n"; for (const auto& shape : shapes) { std::visit(PrintVisitor{}, shape); } // 也可以使用 lambda 直接访问 std::cout << "\n使用 lambda 直接访问 area 和 perimeter:\n"; for (const auto& shape : shapes) { std::visit([](const auto& s) { std::cout << std::fixed << std::setprecision(2) << "Area: " << s.area() << ", Perimeter: " << s.perimeter() << "\n"; }, shape); } // 静态多态单独处理示例 std::cout << "\n静态多态单独处理:\n"; Circle circle(5.0); Rectangle rectangle(4.0, 6.0); Triangle triangle(3.0, 4.0, 5.0); circle.printInfo(); rectangle.printInfo(); triangle.printInfo(); return 0; }

以下是带基类虚函数(用途二):

C++ 17

#include <iostream> #include <vector> #include <cmath> #include <variant> #include <memory> #include <iomanip> // CRTP 基类模板 template <typename Derived> class Shape { public: double area() const { return static_cast<const Derived*>(this)->calculateArea(); } double perimeter() const { return static_cast<const Derived*>(this)->calculatePerimeter(); } void printInfo() const { std::cout << std::fixed << std::setprecision(2) << "Area: " << area() << ", Perimeter: " << perimeter() << "\n"; } }; // 圆形 class Circle : public Shape<Circle> { double radius; public: Circle(double r) : radius(r) {} double calculateArea() const { return M_PI * radius * radius; } double calculatePerimeter() const { return 2 * M_PI * radius; } }; // 矩形 class Rectangle : public Shape<Rectangle> { double width, height; public: Rectangle(double w, double h) : width(w), height(h) {} double calculateArea() const { return width * height; } double calculatePerimeter() const { return 2 * (width + height); } }; // 三角形 class Triangle : public Shape<Triangle> { double a, b, c; public: Triangle(double s1, double s2, double s3) : a(s1), b(s2), c(s3) {} double calculateArea() const { double s = (a + b + c) / 2; return sqrt(s * (s - a) * (s - b) * (s - c)); } double calculatePerimeter() const { return a + b + c; } }; // 使用 std::variant 存储不同类型的形状 using ShapeVariant = std::variant<Circle, Rectangle, Triangle>; // 访问者类,用于调用 variant 中的对象的成员函数 class ShapeVisitor { public: void operator()(const Circle& c) const { std::cout << "Circle: "; c.printInfo(); } void operator()(const Rectangle& r) const { std::cout << "Rectangle: "; r.printInfo(); } void operator()(const Triangle& t) const { std::cout << "Triangle: "; t.printInfo(); } }; int main() { // 创建不同类型的图形对象 Circle circle(5.0); Rectangle rectangle(4.0, 6.0); Triangle triangle(3.0, 4.0, 5.0); std::cout << "单独处理每个形状:\n"; circle.printInfo(); rectangle.printInfo(); triangle.printInfo(); std::cout << "\n使用 std::variant 统一处理:\n"; // 创建 variant 的 vector std::vector<ShapeVariant> shapes; shapes.push_back(circle); shapes.push_back(rectangle); shapes.push_back(triangle); ShapeVisitor visitor; for (const auto& shape : shapes) { std::visit(visitor, shape); } std::cout << "\n使用 lambda 表达式处理 variant:\n"; for (const auto& shape : shapes) { std::visit([](const auto& s) { using T = std::decay_t<decltype(s)>; if constexpr (std::is_same_v<T, Circle>) { std::cout << "Circle: "; } else if constexpr (std::is_same_v<T, Rectangle>) { std::cout << "Rectangle: "; } else if constexpr (std::is_same_v<T, Triangle>) { std::cout << "Triangle: "; } s.printInfo(); }, shape); } return 0; }

C++ 11

#include <iostream> #include <vector> #include <cmath> #include <memory> #include <iomanip> // 非模板基类,用于类型擦除 class IShape { public: virtual double area() const = 0; virtual double perimeter() const = 0; virtual void printInfo() const = 0; virtual ~IShape() = default; }; // CRTP 基类模板 template <typename Derived> class Shape : public IShape { public: double area() const override { return static_cast<const Derived*>(this)->calculateArea(); } double perimeter() const override { return static_cast<const Derived*>(this)->calculatePerimeter(); } void printInfo() const override { std::cout << std::fixed << std::setprecision(2) << "Area: " << area() << ", Perimeter: " << perimeter() << "\n"; } }; // 圆形 class Circle : public Shape<Circle> { double radius; public: Circle(double r) : radius(r) {} double calculateArea() const { return M_PI * radius * radius; } double calculatePerimeter() const { return 2 * M_PI * radius; } }; // 矩形 class Rectangle : public Shape<Rectangle> { double width, height; public: Rectangle(double w, double h) : width(w), height(h) {} double calculateArea() const { return width * height; } double calculatePerimeter() const { return 2 * (width + height); } }; // 三角形 class Triangle : public Shape<Triangle> { double a, b, c; public: Triangle(double s1, double s2, double s3) : a(s1), b(s2), c(s3) {} double calculateArea() const { double s = (a + b + c) / 2; return sqrt(s * (s - a) * (s - b) * (s - c)); } double calculatePerimeter() const { return a + b + c; } }; int main() { // 创建不同类型的图形对象 std::cout << "使用基类指针统一处理:\n"; // 使用基类指针的 vector std::vector<std::unique_ptr<IShape>> shapes; shapes.push_back(std::make_unique<Circle>(5.0)); shapes.push_back(std::make_unique<Rectangle>(4.0, 6.0)); shapes.push_back(std::make_unique<Triangle>(3.0, 4.0, 5.0)); for (const auto& shape : shapes) { shape->printInfo(); } // 单独处理每个形状(静态多态) std::cout << "\n使用静态多态单独处理:\n"; Circle circle(5.0); Rectangle rectangle(4.0, 6.0); Triangle triangle(3.0, 4.0, 5.0); circle.printInfo(); rectangle.printInfo(); triangle.printInfo(); return 0; }
http://www.cnnetsun.cn/news/84157.html

相关文章:

  • 中电金信:智能辅助审单方案让跨境金融审核又快又准
  • 虚拟专用网络门户的恶意扫描激增40倍
  • 3D点云标注效率革命:从单帧耗时到批量产出的实战经验分享
  • 颠覆传统Shell安全思维:构建零信任脚本架构的5大创新策略
  • 基于 Faster RCNN 的工业储罐类型识别与定位_卫星遥感图像分析
  • 为什么 Edge 才是安卓排名第1的浏览器?
  • 开题报告已死?宏智树AI如何帮你完成一个学术起点
  • 瞬间对大模型的兴趣达到100000000000%,太香了!
  • 网军“捡漏”:数据泄露如何助力国家级APT搭建C2基础设施
  • 毕设项目分享 深度学习验证码识别系统(源码+论文)
  • 第一个海底的智算中心,真是敢想敢干
  • 为什么现在很难招到有水平的SLAM工程师?
  • 终极Flutter滚动布局指南:打造流畅动态Header效果
  • 程序员必看:大模型基础原理与GPU并行训练指南(建议收藏)
  • 30分钟快速部署企业级智能管理平台:SmartAdmin完整安装指南
  • 含中间直流的三相电力电子变压器PET仿真模型(Simulink仿真实现)
  • 【面试精选】26年最全网络安全面试,华为大佬带你快速通关面试!!吃透面试成功率96%
  • 转录组研究攻略|常见可视化结果解读
  • 新增AI引擎!快快网络联合集美大学共建工业智能与网络安全创新实验室
  • 5.3 从零构建MCP Server:实现文件处理与数据库访问
  • PapersGPT for Zotero 完整安装与使用指南:让文献管理更智能
  • 7.3 任务分解与管理:利用Cursor Memory Bank和Claude Code自定义命令
  • 中美文化对 AI 意识觉醒的根本差异:文明基因与 AGI 时代的未来路径
  • 豆包AI手机动了谁的“生态命门”?
  • 万字长文,保姆级教程!从零教你优雅开发复杂AI Agent,从入门到精通,看这篇就够了!
  • 标注好的胃病识别数据集,可识别食管炎,胃炎,胃出血,健康,息肉,胃溃疡等常见疾病,支持yolo, coco json,pascal voc xml格式的标注
  • 轻松上手:零基础使用AI智能图表工具制作专业可视化图表
  • 201React-Query:useQuery基本使用
  • 开源可定制的订水小程序系统详解 带完整的搭建部署教程以及搭建指南
  • Qwen-Image-Edit-2509实现任意尺寸图像智能编辑