C++ CRTP 替代虚函数

基本原理:

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

核心思想只有一个东西:

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

不带虚函数(用途一):

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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;
}
相关推荐
kisshyshy14 小时前
🍦 雪糕、食堂、火车厢:三幅漫画吃透栈、队列与链表
javascript·算法
众少成多积小致巨16 小时前
JNI (Java Native Interface) 技术手册中文参考指南
android·java·c++
猿人谷1 天前
不只是 CPU 阈值:STAR 如何用 GAT + Transformer 做容器级自动扩缩容?
人工智能·算法
复杂网络1 天前
Stable Diffusion 视觉大模型微调技术深度调研
算法
复杂网络1 天前
基于 Stable Diffusion 架构的视觉大模型代表性工作与原理深度解析
算法
MrZhao4001 天前
Agent Loop 如何用 Hook 扩展:权限、日志与工具拦截
算法
MrZhao4001 天前
Agent 为什么需要 Skills:别把所有知识都塞进 system prompt
算法
JieE2122 天前
LeetCode 101. 对称二叉树|JS 递归 + 迭代双解法,彻底搞懂镜像判断
javascript·算法
JieE2123 天前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
Jack204 天前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法