# 类型擦除
类型擦除是 C++ 中一种用于实现多态性的编程技术,它允许在不牺牲性能或引入不必要的运行时开销的情况下进行多态性操作。 通过隐藏对象的实际类型并提供统一的接口,类型擦除使得可以以多态的方式处理不同类型的对象,同时在运行时推迟对实际类型的确定。
# 类 (Class)
首先来说类的类型擦除,来看一个例子:
#include <iostream> | |
class fruit { | |
public: // 确保访问修饰符是 public,以便派生类可以访问 | |
virtual void eat() = 0; // 纯虚函数 | |
}; | |
class apple : public fruit { | |
public: | |
void eat() override { | |
std::cout << "eat apple" << std::endl; | |
} | |
}; | |
class watermelon : public fruit { | |
public: | |
void eat() override { | |
std::cout << "eat watermelon" << std::endl; | |
} | |
}; | |
class banana : public fruit { | |
public: | |
void eat() override { | |
std::cout << "eat banana" << std::endl; | |
} | |
}; | |
int main() { | |
apple a; | |
banana b; | |
watermelon w; | |
fruit *p = &a; // 类型擦除(apple 类型被转换成了 fruit 基类) | |
p->eat(); // 运行 apple 的 eat () | |
p = &b; | |
p->eat(); // 运行 banana 的 eat () | |
p = &w; | |
p->eat(); // 运行 watermelon 的 eat () | |
return 0; | |
} |
运行结果:
eat apple | |
eat banana | |
eat watermelon |
可以看到,通过这种方法,我们可以通过一个统一的入口 fruit
来动态的调用其实际子类的函数,这就是一种通过类型擦除来实现多态的方法。
# 类型 (Type)
C++ 提供了 std::any
来表示任意类型的数据,并可以通过 std::any_cast
来在运行中动态的将 std::any
转换成正确的类型,结合上述类的类型擦除和模版,我们就可以做到一些很酷的事:
#include <any> | |
#include <stdexcept> | |
class any_convert_base { | |
public: | |
virtual void convert(std::any val) = 0; | |
}; | |
template <typename T> | |
class any_convert : public any_convert_base { | |
public: | |
void convert(std::any val) override { | |
try { | |
value = std::any_cast<T>(val); | |
} catch (const std::bad_any_cast&) { | |
// 处理转换失败的情况 | |
throw std::runtime_error("Failed to cast std::any to type T"); | |
} | |
} | |
T value; | |
}; | |
int main() { | |
any_convert_base* p; | |
std::any i = 123; // 类型擦除 | |
any_convert<int> a; | |
p = &a; | |
p->convert(i); // 调用成员函数,接受任意类型的变量并尝试转换成需要的类型存储 | |
return 0; | |
} |
⚠️ 注意,由于 std::any
在 c++17
中才引入,所以如果编译失败,可以尝试加上 -std=c++17
后再进行编译。
通过上述代码,我们实现了给 any_convert
类的 convert
方法一个任意类型的参数, convert
可以自动将其转换成正确的类型。在此基础上,我们只需要再实现一个字符串到类型的注册机制,就可以在 C++
中实现 Java
的反射机制。
总的来说,虽然类型擦除无法让 c++
像弱类型语言一样灵活,但确实可以赋予 C++
运行时多态的能力,在充分享受 C++
强类型的安全行 = 性、高效性的同时,提供更大的运行时灵活设计的空间,减轻了雷同代码逻辑的编写任务。