news 2026/5/31 0:15:26

C++ CRTP 替代虚函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/29 20:40:48

Qwen-Image低显存部署与中文海报生成

Qwen-Image低显存部署与中文海报生成&#xff1a;从模型镜像到专业级视觉创作实战 你有没有遇到过这样的场景&#xff1f;客户发来一条需求&#xff1a;“做个端午节活动海报&#xff0c;要有‘端午安康’四个字&#xff0c;风格传统一点&#xff0c;还得带点现代感。” 于是你…

作者头像 李华
网站建设 2026/5/29 0:02:59

开源项目版本管理终极指南:告别分支混乱与代码冲突

开源项目版本管理终极指南&#xff1a;告别分支混乱与代码冲突 【免费下载链接】qmk_firmware Open-source keyboard firmware for Atmel AVR and Arm USB families 项目地址: https://gitcode.com/GitHub_Trending/qm/qmk_firmware 你是否曾在深夜调试代码时&#xff0…

作者头像 李华
网站建设 2026/5/29 20:38:33

露,机能实验室整体解决方案 行为学实验室整体解决方案 动物行为学整体解决方案 人体生理实验整体解决方案

在医学教育中引入生理实验&#xff0c;有助于打破临床与基础阶段的早期壁垒&#xff1a;学生通过亲身参与相互性自身实验&#xff0c;深化对基础实验意义的认知&#xff0c;同时积累临床诊断的直观感受&#xff0c;安徽&#xff0c;正华&#xff0c;生物动物行为实验站属于综合…

作者头像 李华
网站建设 2026/5/29 19:51:28

GPON OLT 和 EPON OLT 刚入门怎么选?

对于很多小白来说&#xff0c;不从事光模块行业&#xff0c;不了解GPON OLT 和 EPON OLT光模块的不同到底在哪里&#xff0c;更不知道怎么去选择更合适自己的产品&#xff0c;但新项目测试急需确定&#xff0c;怎么根据项目需求进行选择呢&#xff1f;项目催的急&#xff0c;选…

作者头像 李华
网站建设 2026/5/30 23:53:42

企业级AI知识库革命:如何用开源方案构建永不遗忘的“数字大脑“

2025年Q4&#xff0c;某跨国科技公司的CTO在内部审计时发现一个触目惊心的事实&#xff1a;公司每年因知识流失造成的损失高达2300万元——核心工程师离职带走关键经验、重复技术问题消耗大量人力、新项目频繁踩前人已踩过的坑。更令人震惊的是&#xff0c;这些本可以通过一套完…

作者头像 李华