我的代码表现得很糟糕,我可以使用下面的代码进行迷你重现。 (codepad link)
来自http://www.cppreference.com/wiki/keywords/dynamic_cast
如果您尝试强制转换为指针 类型,那种类型不是实际的 参数对象的类型,然后是 演员的结果将为NULL。
根据我的理解,this_test应为null。不是。我如何检查虚拟ptr是否实际上是虚拟对象的ptr?
#include <ios>
struct Dummy{ virtual void dummyfn(){} };
int main(){
Dummy* this_test = dynamic_cast<Dummy*>((Dummy*)0x123);
//assert(this_test==0);
cout << std::hex << this_test<<endl;
return 0;
}
输出:
0x123
答案 0 :(得分:2)
我相信dynamic_cast
仅适用于多态情况中的向下投射,而不适用于任何投射。它不像编译器存储每个变量的类型信息,所以它不能做你想的 - 我很确定它是未定义的行为。
答案 1 :(得分:1)
你从你的引语中跳过了一句话:
dynamic_cast
关键字将object
从一个指针或引用类型转换为另一个,执行运行时检查以确保转换的有效性。
这里的问题是0x123不是指向对象的指针,所以它不起作用。
答案 2 :(得分:1)
问题是dynamic_cast
要求:
在这里你只能提供垃圾,所以它没用,而不是你想要的演员。
如果你得到void*
,那么你可以使用reinterpret_cast
(比C-cast更好,因为更明显)将其转换为另一种类型:
void* p = 0x123;
Dummy* dummy = reinterpret_cast<Dummy*>(p);
注意:此处不会注意到虚拟方法的存在与否
编辑:如果您可以修改传递的对象......
然后尝试使用公共基类:
struct Base: private boost::noncopyable { virtual ~Base() = 0 }; Base::~Base() {}
并定义以下帮助程序:
template <typename T>
void* to_void(T* t) {
Base* base = t;
return reinterpret_cast<void*>(base);
}
template <typename T>
T* from_void(void* p) {
Base* base = reinterpret_cast<Base*>(p);
return dynamic_cast<T*>(base);
}
前者非常重要,因为可能的指针调整(这可能只发生在多重继承的情况下)。
注意:如果你不使用虚拟继承或其他RTTI东西,可以在这里使用fast_cast
template <typename T, typename U>
T* fast_cast(U* u) {
#ifdef NDEBUG
return static_cast<T*>(u);
#else
return dynamic_cast<T*>(u);
#endif
}
如果无法做到这一点,可以采用以下解决方案,但我担心它们会让人感觉不舒服。
由于dynamic_cast
在这里不能正常工作,你必须实际提出自己的类型检查机制。
一种方法可以是使用“存储库”,在其中注册所获得的void*
指针以及关联的type_info
对象。
typedef std::map<void*, std::type_info const*> Repository;
template <typename Dest>
Dest* dynamic_check(void* p, Repository const& rep) {
Repository::const_iterator it = rep.find(p);
assert(it != rep.end() && "dynamic_check: no such entry");
assert(typeid(Dest) == *(it->second) && "dynamic_check: wrong type");
return reinterpret_cast<Dest*>(p);
}
如果无法做到这一点,那么你可以破解C ++对象模型。如果您知道该对象至少有一个虚方法,那么它必须在我知道的所有编译器上有一个虚拟指针(VS,gcc,clang),并且该指针是该对象的前4/8字节。
inline void* virtual_pointer(void* p) {
assert(p != 0 && "virtual_pointer: null");
return reinterpret_cast<void*>(*p);
}
template <typename T>
void* virtual_pointer(T const& t) {
return virtual_pointer(reinterpret_cast<void*>(&t));
}
template <typename T>
void* virtual_pointer() {
static void* pointer = virtual_pointer(T());
return pointer;
}
template <typename Dest>
Dest* dynamic_check(void* p) {
assert(virtual_pointer<Dest>() == virtual_pointer(p));
return reinterpret_cast<Dest*>(p);
}
注意:两种解决方案都存在相同的缺点,只有在您精确确定完全类型的情况下它们才能正常工作(好吧,只要有两个,你就可以使用它类型共享相同的虚拟表,如果派生类不覆盖任何虚方法,包括析构函数,则会发生这种情况。
这远不是真正dynamic_cast
的力量。
答案 3 :(得分:0)
实际上dynamic_cast
仅适用于多态类型(通常这意味着它们必须具有vtable)。由于您使用C-cast向编译器声明类型为Dummy*
,因此它相信您。因为您在随机内存位置上进行了身份dynamic_cast
,所以它不能/不能进行类型检查。
但严重的是,99%的时间都没有尝试测试某种特定类型的东西。设计你的类,使基类定义一个接口,子类实现它,允许使用而不需要大量的转换(这是一种设计气味)。
答案 4 :(得分:0)
dynamic_cast
不会执行任何运行时检查。对于此类演员,dynamic_cast
的行为与普通static_cast
完全相同。没有任何区别。
您链接的页面没有提及,即使是dynamic_cast
的远程完整规范,这也使它变得毫无用处。
C ++没有办法确定给定指针实际上是否是指向给定类型的有效指针。所以,你在那里运气不好。实施自己的检查方法。