基本原理:
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;
}