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;
}
相关推荐
木井巳7 小时前
【递归算法】二叉搜索树中第K小的元素
java·算法·leetcode·深度优先·剪枝
铉铉这波能秀7 小时前
LeetCode Hot100 中 enumerate 函数的妙用(2026.2月版)
数据结构·python·算法·leetcode·职场和发展·开发
墨有6667 小时前
哈希表从入门到实现,一篇吃透!
数据结构·算法·哈希算法
Yu_Lijing7 小时前
网络复习篇——网络基础(一)
网络·c++·笔记
Bella的成长园地7 小时前
为什么c++中的条件变量的 wait() 函数需要配合while 循环或谓词?
c++·面试
We་ct7 小时前
LeetCode 228. 汇总区间:解题思路+代码详解
前端·算法·leetcode·typescript
charlee447 小时前
为什么现代 C++ 库都用 PIMPL?一场关于封装、依赖与安全的演进
c++·智能指针·raii·pimpl·编译防火墙·封装设计
AIpanda8887 小时前
如何借助AI销冠系统提升数字员工在销售中的成效?
算法
啊阿狸不会拉杆8 小时前
《机器学习导论》第 7 章-聚类
数据结构·人工智能·python·算法·机器学习·数据挖掘·聚类
木非哲8 小时前
机器学习--从“三个臭皮匠”到 XGBoost:揭秘 Boosting 算法的“填坑”艺术
算法·机器学习·boosting