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;
}
相关推荐
灵感__idea1 天前
Hello 算法:众里寻她千“百度”
前端·javascript·算法
Wect1 天前
LeetCode 130. 被围绕的区域:两种解法详解(BFS/DFS)
前端·算法·typescript
NAGNIP2 天前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
端平入洛2 天前
delete又未完全delete
c++
颜酱2 天前
单调栈:从模板到实战
javascript·后端·算法
CoovallyAIHub2 天前
仿生学突破:SILD模型如何让无人机在电力线迷宫中发现“隐形威胁”
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
从春晚机器人到零样本革命:YOLO26-Pose姿态估计实战指南
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
Le-DETR:省80%预训练数据,这个实时检测Transformer刷新SOTA|Georgia Tech & 北交大
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
强化学习凭什么比监督学习更聪明?RL的“聪明”并非来自算法,而是因为它学会了“挑食”
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
YOLO-IOD深度解析:打破实时增量目标检测的三重知识冲突
深度学习·算法·计算机视觉