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;
}
相关推荐
stellanke21 分钟前
Linux网络编程实战2:TCP Socket 基础与实践
linux·网络·c++
手写码匠29 分钟前
DeepSeek 函数调用实战:从零搭建一个会“动手“的 AI 助手
人工智能·深度学习·算法·aigc
蒸蒸yyyyzwd32 分钟前
机试和Redis学习笔记
c++·笔记·面试
tdtsmt1 小时前
2026年SMT贴片加工选型指南:天地通电子推荐参考
c++
果壳science1 小时前
《自然本源》如何实现狭义相对论的代数化
笔记·算法·几何学
一木 之林1 小时前
插件、MCP、Skill 的区别?
java·c++·人工智能
suhan2 小时前
【数据结构】二、线性表:2.单链表的定义与实现(带头结点与不带头结点的区别)
数据结构
ShineWinsu2 小时前
对于MySQL:内置函数的解析
linux·数据库·c++·mysql·面试·函数·查询
302wanger2 小时前
分享一个工作习惯:整点看即时消息
算法
洋不写bug4 小时前
排序(一)基础排序,插入|希尔|冒泡|直接选择排序详解
java·算法·排序算法·插入排序·冒泡排序·希尔排序·直接选择排序