百度网盘私人资源链接,seo北京公司,制作灯笼的手工做法简单漂亮,付网站建设服务费什么科目目录
一、核心原理
二、常见实现方式
1. 基于继承和虚函数#xff08;经典方式#xff09;
2. 基于 std::function#xff08;现代方式#xff09;
3. 基于模板和静态多态#xff08;编译时类型擦除#xff09;
三、应用场景
四、优缺点
优点#xff1a;
缺点经典方式
2. 基于 std::function现代方式
3. 基于模板和静态多态编译时类型擦除
三、应用场景
四、优缺点
优点
缺点
五、标准库中的类型擦除示例
总结 C 类型擦除Type Erasure是一种编程技术用于在保持运行时多态的同时隐藏具体类型信息使代码更灵活、更通用。它通过封装类型特定的实现细节将动态类型转换的负担从编译时转移到运行时。以下从原理、实现方式和应用场景三个方面详细介绍
一、核心原理
类型擦除的本质是分离接口与实现
定义统一接口通过抽象基类或函数对象定义公共行为。封装具体实现将不同类型的实现细节封装在内部对外提供统一接口。运行时多态通过虚函数或函数指针在运行时动态调用具体实现。
与传统继承多态的区别
继承多态依赖公共基类客户端需知晓具体派生类。类型擦除客户端仅与接口交互完全 unaware 具体类型。
二、常见实现方式
1. 基于继承和虚函数经典方式
通过抽象基类定义接口派生类实现具体逻辑外部使用智能指针管理对象。
示例代码
#include iostream // 添加对头文件的包含
#include memory
#include string// 定义接口
class Shape {
public:virtual ~Shape() default;virtual double area() const 0;virtual std::string name() const 0;
};// 具体实现类
class Circle : public Shape {
public:explicit Circle(double r) : radius(r) {}double area() const override { return 3.14 * radius * radius; }std::string name() const override { return Circle; }
private:double radius;
};class Rectangle : public Shape {
public:Rectangle(double w, double h) : width(w), height(h) {}double area() const override { return width * height; }std::string name() const override { return Rectangle; }
private:double width, height;
};// 类型擦除包装器
class AnyShape {
public:templatetypename TAnyShape(T shape) : self(std::make_uniqueModelT(std::move(shape))) {}double area() const { return self-area(); }std::string name() const { return self-name(); }private:struct Concept {virtual ~Concept() default;virtual double area() const 0;virtual std::string name() const 0;};templatetypename Tstruct Model : Concept {explicit Model(T value) : data(std::move(value)) {}double area() const override { return data.area(); }std::string name() const override { return data.name(); }T data;};std::unique_ptrConcept self;
};// 使用示例
int main() {AnyShape s1 Circle(5.0);AnyShape s2 Rectangle(3.0, 4.0);// 客户端无需知道具体类型统一调用接口std::cout s1.name() : s1.area() std::endl; // 输出 Circle: 78.5std::cout s2.name() : s2.area() std::endl; // 输出 Rectangle: 12
}
关键点
AnyShape 是类型擦除包装器内部持有指向 Concept 接口的指针。ModelT 是具体实现的适配器将任意符合接口的类型 T 转换为统一的 Concept。
2. 基于 std::function现代方式
利用 std::function 内置的类型擦除能力直接存储可调用对象。
示例代码
#include functional
#include string
#include vectorclass AnyShape {
public:templatetypename TAnyShape(T shape) : area_func([shape]() { return shape.area(); }),name_func([shape]() { return shape.name(); }) {}double area() const { return area_func(); }std::string name() const { return name_func(); }private:std::functiondouble() area_func;std::functionstd::string() name_func;
};// 使用示例
struct Triangle {double base, height;double area() const { return 0.5 * base * height; }std::string name() const { return Triangle; }
};int main() {std::vectorAnyShape shapes;shapes.push_back(Triangle{3.0, 4.0});shapes.push_back(Circle{5.0}); // 复用前面的Circle类for (const auto shape : shapes) {std::cout shape.name() : shape.area() std::endl;}
}
关键点
std::function 可以存储任何可调用对象函数、lambda、成员函数等。通过 lambda 捕获具体对象将其方法转换为无参数的可调用对象。
3. 基于模板和静态多态编译时类型擦除
使用模板在编译时实现类型擦除避免运行时开销。
示例代码
templatetypename Shape
double calculate_area(const Shape shape) {return shape.area(); // 静态多态依赖Shape类型有area()方法
}// 使用示例
struct Square {double side;double area() const { return side * side; }
};int main() {Square s{5.0};std::cout calculate_area(s) std::endl; // 编译时确定类型
}
特点
编译时多态无虚函数调用开销。要求所有类型实现统一接口鸭子类型。
三、应用场景 泛型容器存储不同类型但具有相同接口的对象。
例如std::vectorAnyShape 可存储 Circle、Rectangle 等任意形状。 插件系统动态加载不同实现统一接口调用。
例如游戏引擎加载不同厂商的渲染插件。 回调函数封装隐藏回调函数的具体类型。
例如GUI 框架的事件处理系统存储不同类型的事件回调。 跨库接口在不同库之间传递对象避免暴露具体类型。
例如数据库驱动返回统一的 RecordSet 接口隐藏底层实现。
四、优缺点
优点
解耦接口与实现客户端无需依赖具体类型降低编译依赖。灵活性可在运行时动态切换实现。代码复用同一套逻辑处理多种类型。
缺点
性能开销虚函数调用或 std::function 的间接调用会降低性能。类型信息丢失无法恢复原始类型除非手动存储类型标签。实现复杂度需要设计额外的包装层代码可读性可能降低。
五、标准库中的类型擦除示例
std::function可存储任何可调用对象擦除具体函数类型。std::any可存储任何类型的值运行时查询类型。std::vectorbool特殊实现擦除了 bool 的真实类型使用位压缩。
总结
类型擦除是 C 中实现 “运行时泛型” 的强大技术通过隐藏具体类型信息提供统一接口使代码更灵活、更具扩展性。选择合适的实现方式继承、std::function 或模板取决于具体场景的性能需求和类型安全要求。