Safely checking the type of a variable
对于系统,我需要将指针转换为long类型,然后再将long类型转换为指针类型。如您所料,这是非常不安全的。我想做的是使用dynamic_cast进行转换,因此,如果我将它们混合在一起,将会得到一个空指针。该页面显示http://publib.boulder.ibm.com/infocenter/lnxpcomp/v7v91/index.jsp?topic=/com.ibm.vacpp7l.doc/language/ref/clrc05keyword_dynamic_cast.htm
The dynamic_cast operator performs
type conversions at run time. The
dynamic_cast operator guarantees the
conversion of a pointer to a base
class to a pointer to a derived class,
or the conversion of an lvalue
referring to a base class to a
reference to a derived class. A
program can thereby use a class
hierarchy safely. This operator and
the typeid operator provide run-time
type information (RTTI) support in
C++.
我想得到一个错误,如果它为null,那么我写了自己的动态演员表
1 2 3 4 5 | template<class T, class T2> T mydynamic_cast(T2 p) { assert(dynamic_cast< T >(p)); return reinterpret_cast< T >(p); } |
使用MSVC时,出现错误"错误C2681:'long':dynamic_cast的无效表达式类型"。事实证明,这仅适用于具有虚拟功能的类……WTF!我知道动态类型转换的目的是针对上/下类型转换继承问题,但我也认为这是动态解决类型转换问题。我知道我可以使用reinterpret_cast,但是不能保证相同的安全性。
我应该使用什么检查我的类型转换是否为相同类型?我可以比较两个typeid,但是当我想将派生类型转换为它的基数时会遇到问题。那么我该如何解决呢?
另一种选择是使用联合:
1 2 3 4 | union U { int* i_ptr_; long l; } |
同样,很少也需要工会。
在以仅支持C接口的语言编写的应用程序中加载C ++ DLL时,我不得不做类似的事情。这是一个解决方案,如果传入了意外的对象类型,将立即给您带来错误。这可以使发生问题时更容易诊断。
诀窍在于,您作为句柄传递的每个类都必须从通用基类继承。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | #include <stdexcept> #include <typeinfo> #include <string> #include <iostream> using namespace std; // Any class that needs to be passed out as a handle must inherit from this class. // Use virtual inheritance if needed in multiple inheritance situations. class Base { public: virtual ~Base() {} // Ensure a v-table exists for RTTI/dynamic_cast to work }; class ClassA : public Base { }; class ClassB : public Base { }; class ClassC { public: virtual ~ClassC() {} }; // Convert a pointer to a long handle. Always use this function // to pass handles to outside code. It ensures that T does derive // from Base, and that things work properly in a multiple inheritance // situation. template <typename T> long pointer_to_handle_cast(T ptr) { return reinterpret_cast<long>(static_cast<Base*>(ptr)); } // Convert a long handle back to a pointer. This makes sure at // compile time that T does derive from Base. Throws an exception // if handle is NULL, or a pointer to a non-rtti object, or a pointer // to a class not convertable to T. template <typename T> T safe_handle_cast(long handle) { if (handle == NULL) throw invalid_argument(string("Error casting null pointer to") + (typeid(T).name())); Base *base = static_cast< T >(NULL); // Check at compile time that T converts to a Base * base = reinterpret_cast<Base *>(handle); T result = NULL; try { result = dynamic_cast< T >(base); } catch(__non_rtti_object &) { throw invalid_argument(string("Error casting non-rtti object to") + (typeid(T).name())); } if (!result) throw invalid_argument(string("Error casting pointer to") + typeid(*base).name() +" to" + (typeid(T).name())); return result; } int main() { ClassA *a = new ClassA(); ClassB *b = new ClassB(); ClassC *c = new ClassC(); long d = 0; long ahandle = pointer_to_handle_cast(a); long bhandle = pointer_to_handle_cast(b); // long chandle = pointer_to_handle_cast(c); //Won't compile long chandle = reinterpret_cast<long>(c); // long dhandle = pointer_to_handle_cast(&d); Won't compile long dhandle = reinterpret_cast<long>(&d); // send handle to library //... // get handle back try { a = safe_handle_cast<ClassA *>(ahandle); //a = safe_handle_cast<ClassA *>(bhandle); // fails at runtime //a = safe_handle_cast<ClassA *>(chandle); // fails at runtime //a = safe_handle_cast<ClassA *>(dhandle); // fails at runtime //a = safe_handle_cast<ClassA *>(NULL); // fails at runtime //c = safe_handle_cast<ClassC *>(chandle); // Won't compile } catch (invalid_argument &ex) { cout << ex.what() << endl; } return 0; } |
请记住,在Windows 64中,指针将是64位数量,但
至于其余的,其他人已经充分说明了
另外,最好使用size_t而不是long -我认为可以确保此类型与地址空间的大小兼容。
您想做的事情听起来像是一个非常糟糕和危险的主意,但是如果您必须这样做(即您正在使用旧系统或在永远不会改变的硬件上工作),那么我建议将指针包装在一些一种简单的结构,包含两个成员:1)指向对象实例的void指针以及一个字符串,枚举或某种其他种类的唯一标识符,这些标识符将告诉您将原始void *强制转换为什么。这是我的意思的示例(注意:我没有打扰过测试,因此其中可能存在语法错误):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | struct PtrWrapper { void* m_theRealPointer; std::string m_type; }; void YourDangerousMethod( long argument ) { if ( !argument ) return; PtrWrapper& pw = *(PtrWrapper*)argument; assert( !pw.m_type.empty() ); if ( pw.m_type =="ClassA" ) { ClassA* a = (ClassA*)pw.m_theRealPointer; a->DoSomething(); } else if (...) { ... } } |
reinterpret_cast是在此处使用的正确转换。
这几乎是它唯一可以安全执行的操作。
从指针类型到指针T并返回原始指针类型的reinterpret_cast会产生原始指针。 (假设T是至少与原始指针类型一样大的指针或整数类型)
注意,未指定从指针类型到T的reinterpret_cast。 T类型的值没有任何保证,除了如果您随后将其reinterpret_cast转换回原始类型,则可以得到原始值。因此,假设您不尝试对中间long值进行任何操作,那么reinterpret_cast是非常安全且可移植的。
编辑:当然,如果您在第二次转换时不知道原始类型是什么,这将无济于事。那样的话,你就被搞砸了。 long不能以任何方式携带有关从其转换指针的类型信息。
您可以使用
就像其他人已经说过的那样,在非多态类上使用dynamic_cast并不是定义好的行为(除非您进行向上转换,否则无论如何都是隐式的,在这里将被忽略),它也仅适用于指针或引用。不在整数类型上。
最好使用在各种posix系统上找到的
关于检查转换是否成功,可以使用sizeof:
1 | BOOST_STATIC_ASSERT(sizeof(T1) >= sizeof(T2)); |
如果无法完成转换,将在编译时失败。或者继续在该条件下使用断言,它将在运行时断言。
警告:这不会阻止您使用U以外的其他类型将
为了澄清我的意思:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | struct a_kind {}; struct b_kind {}; void function(long ptr) {} int main(int argc, char *argv[]) { a_kind * ptr1 = new a_kind; b_kind * ptr2 = new b_kind; function( (long)ptr1 ); function( (long)ptr2 ); return 0; } |
- long由带有某些类型信息的对象包装。
- 类型本身在引用的对象中编码。
两种解决方案都很丑陋,应避免使用,因为RTTI是替代方案。
一旦您决定将指针指向长整数,就立即将类型安全性扔给了风。
dynamic_cast用于转换衍生树。即,从基类指针到派生类指针。如果你有:
1 2 3 4 5 6 7 8 9 10 11 | class Base { }; class Foo : public Base { }; class Bar : public Base { }; |
您可以通过这种方式使用dynamic_cast ...
1 2 3 4 5 6 7 | Base* obj = new Bar; Bar* bar = dynamic_cast<Bar*>(obj); // this returns a pointer to the derived type because obj actually is a 'Bar' object assert( bar != 0 ); Foo* foo = dynamic_cast<Foo*>(obj); // this returns NULL because obj isn't a Foo assert( foo == 0 ); |
...但是您不能使用动态类型转换将其强制转换到派生树之外。为此,您需要reinterpret_cast或C样式强制转换。