类型擦除就是将原有类型消除或隐藏。类型擦除可以使得程序获得更好的扩展性,消除耦合。
下面是常见的类型擦除方式:
1 多态
2 模板
3 类型容器Variant
4 通用类型Any
5 闭包
1 多态
通过将派生类型隐式转换为基类型,再通过基类去调用虚函数。这样就把派生类型转成基类型隐藏起来。多态方式的类型擦除只是部分擦除,因为基类型还在;且需要继承关系,强耦合。
2 模板
本质上是把不同类型的共同行为进行了抽象,不需要再通过继承这种强耦合方式去获取共同的行为。存在的问题是,不能把T本身作为容器元素,必须在容器初始化时指定T为某个具体类型。
3 类型容器Variant
Variant可以把各种不同的类型包起来,获得一种统一的类型,它是个类型容器。例如,可以通过Variant这样擦除类型:
typedef Variant<double, int, uint32_t, char*> Value;
vector<Value> vt;
vt.push_back(1);
vt.push_back("test");
vt.push_back(1.22);
通过Get<T>()就可以获取对应类型的值。这种方式通过把类型包起来,达到类型擦除的目的。缺点是通用的类型必须提前定义好。
4 通用类型Any
通用类型Any类似Java中的Object,介绍用Any类型擦除的方法:
vector<Any> v;
v.push_back(1);
v.push_back("test");
v.push_back(2.35);
auto r1 = v[0].AnyCast<int>();
auto r2 = v[1].AnyCast<const char*>();
auto r3 = v[2].AnyCast<double>();
Any允许任何类型的对象都赋值给Any对象。缺点是取值的时候仍需要具体的类型,可通过闭包改进。
5 闭包
闭包就是能够读取其他函数内部变量的函数。如lambda表达式。闭包将类型信息保存在闭包中,从而隐藏了类型信息,实现类型擦除的目的。因为闭包本身类型是确定的,可以放到普通容器中,在需要时可从闭包中取出具体的类型。看下如何通过闭包擦除类型:
template<typename T>
void Func(T t)
{
cout << t << endl;
}
void TestErase()
{
int x = 1;
char y = 's';
vector<std::function<void()>> v;
// 类型擦除,闭包中隐藏了具体的类型(x,y)
v.pushback([x]{ Func(x); });
v.pushback([y]{ Func(y); });
// 遍历闭包,取出实际的参数
for (auto item:v)
{
item();
}
}
IoC容器主要用到通用类型Any和闭包来擦除类型。