dynamic_cast
用于将多态对象转换为具有您尝试转换为父对象的对象类型的类。
void*
与此完全不同。使用指向 void 的指针,您实际上是在剥离每种类型信息。
dynamic_cast
知道有一个基类,可以通过 RTTI 进行类型检查。
当你放弃一个 void 指针时,你是在对编译器说:“是的,你知道内存中的这个位置吗?好吧,将它用作这种类型”,如果内存无效,则调用 UB。
你在这里有三个选择。
选项 1
使用接口。好吧,多态基类是实现dynamic_cast
. 没有其他方法,没有黑客,这是唯一的方法。就那么简单。
struct Base { virtual ~Base() = default; };
struct Derived : Base {};
// ...
void test (Base* base) {
auto derived = dynamic_cast<Derived*>(base);
if (derived) {
// derived is valid here.
}
}
选项 2
用指针识别类型我使用一种方法来为每种类型提供一个唯一标识符,并使用该标识符来验证强制转换。无需任何 RTTI 即可完成
using type_id_t = void(*)();
template <typename T> void type_id() {}
// now we can use a map or a vector.
std::vector<std::pair<type_id_t, void*>> vec;
template<typename T>
void insertToMyVector(T* obj) {
vec.emplace_back(type_id<T>, obj);
}
template<typename T>
T* getObj(int index) {
auto item = vec[index];
return static_cast<T*>(item.first == &type_id<T> ? item.second : nullptr);
}
// ...
int main () {
auto foo = new Foo;
insertToMyVector(foo);
auto maybeFoo = getObj<Foo>(0);
if (maybeFoo) {
// you have a valid Foo here
}
}
选项 3
为任何类型生成派生类 这个非常有用,因为它可以保存任何类型,同时保持类型安全。我看起来像解决方案 1,但提供了更多的灵活性。它使用模板为任何类型生成派生类的技巧。优点是你可以持有任何类型,但可能会使你复杂一点。
struct Base { virtual ~Base() = default; };
template<typename T>
struct Derived : Base {
Derived(T&& obj) : _obj{std::move(obj)} {}
Derived(const T& obj) : _obj{obj} {}
T& get() {
return _obj;
}
const T& get() const {
return _obj;
}
private:
T _obj;
};
// ...
void test (Base* base) {
auto derived = dynamic_cast<Derived<int>*>(base);
if (derived) {
int i = derived->get();
// derived is valid here, and we can safely access the int
}
}