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;
}
相关推荐
电摇小人2 小时前
莫比乌斯反演详细解说来啦!!!
数据结构·算法
7澄12 小时前
数据结构进阶:从链表到红黑树(二叉树、平衡树、红黑树核心解析)
数据结构·链表·二叉树·红黑树·平衡二叉树·旋转·二叉排序树
buyue__2 小时前
C++实现数据结构——队列
数据结构·c++
Hui Baby2 小时前
LSM 原理、实现及与 B+ 树的核心区别
java·linux·算法
NZT-482 小时前
C++基础笔记(二)队列deque,queue和堆priority_queue
java·c++·笔记
爬山算法2 小时前
Netty(13)Netty中的事件和回调机制
java·前端·算法
hweiyu002 小时前
数据结构:广义表
数据结构
CoovallyAIHub3 小时前
是什么支撑L3自动驾驶落地?读懂AI驾驶与碰撞预测
深度学习·算法·计算机视觉
玉树临风ives3 小时前
atcoder ABC436 题解
c++·算法·leetcode·atcoder·信息学奥赛