题
对于一个系统,我需要换一个指向一个长再长回来的指针的类型。你可以猜测这是非常不安全。什么我想要做的就是用dynamic_cast做转换因此,如果我混入他们我会得到一个空的指针。这一页上说 http://publib.boulder.ibm.com/infocenter/lnxpcomp/v7v91/index.jsp?topic=/com.ibm.vacpp7l.doc/language/ref/clrc05keyword_dynamic_cast.htm
该dynamic_cast操作者进行 类型转换在运行时间。的 dynamic_cast运营商保证 转换指向一个基 一流的一个指派生类, 或者转左值 参照基类 参考来源类。一个 程序可以由此使用的一类 层次的安全。这个操作者和 该typeid操作人员提供运行时间 类型的信息(RTTI)支持在 C++。
和我想得到一个错误,如果它null所以我写了我自己的动态转换
template<class T, class T2> T mydynamic_cast(T2 p)
{
assert(dynamic_cast<T>(p));
return reinterpret_cast<T>(p);
}
与MSVC我得到的错误"的错误C2681:"长":无效表达的类型dynamic_cast".事实证明这只会的工作类具有虚拟的功能...跆拳道!我知道这点的一个动态的投被用的上/下铸造的继承问题,但我也认为这是解决类型转换的问题动态。我知道我可以用reinterpret_cast但这并不能保证同一类型的安全。
我应该使用什么检查,如果我的类型转换的是同一类型?我可以比较这两个typeid但我想有一个问题时,我想类型转换的一个衍生的,其基础。所以我怎么可以解决这个?
解决方案
在用仅支持C接口的语言编写的应用程序中加载C ++ DLL时,我不得不做类似的事情。如果传入了意外的对象类型,这个解决方案将立即给您带来错误。这可以使出现问题时更容易诊断。
诀窍在于,作为句柄传递的每个类都必须从公共基类继承。
#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;
}
其他提示
dynamic_cast
只能在通过继承相关的类之间使用。要将指针转换为long(反之亦然),可以使用reinterpret_cast
。要检查指针是否为空,可以assert(ptr != 0)
。但是,通常不建议使用<=>。为什么需要将指针转换为long?
另一个选择是使用联合:
union U {
int* i_ptr_;
long l;
}
同样,联盟也很少需要。
请记住,在Windows 64中,指针将是64位数量,但long
仍然是32位数量,并且您的代码已损坏。至少,您需要根据平台选择整数类型。我不知道MSVC是否支持uintptr_t
,C99中提供的用于保持指针的类型;如果它可用,这将是最好的类型。
至于其他人,其他人已经充分解决了dynamic_cast
vs reinterpret_cast
的原因和原因。
reinterpret_cast是在这里使用的正确演员。
这几乎是唯一它可以安全地做的事情。
从指针类型到类型T的reinterpret_cast并返回到原始指针类型会产生原始指针。 (假设T是指针或整数类型,至少与原始指针类型一样大)
请注意,未指定从指针类型到T的reinterpret_cast。无法保证T类型的值,除之外,如果您将其重新解释为广播类型,则会得到原始值。因此,假设您没有尝试使用中间长值进行任何操作,reinterpret_cast非常安全且便携。
编辑:当然,如果您在第二次演员时不知道原始类型是什么,这无济于事。在那种情况下,你被搞砸了。 long不可能以任何方式携带有关从哪个指针转换的类型信息。
您可以使用reinterpret_cast
转换为整数类型并返回指针类型。 如果整数类型足以存储指针值,那么该转换不会改变指针值。
正如其他人已经说过的那样,在非多态类上使用dynamic_cast并不是定义的行为(除非你做了一个upcast,无论如何都是隐式的,在这里被忽略),它也只适用于指针或引用。不是整数类型。
您最好在各种posix系统中使用::intptr_t
。您可以将该类型用作您投射到的中间类型。
关于检查转换是否成功,您可以使用sizeof:
BOOST_STATIC_ASSERT(sizeof(T1) >= sizeof(T2));
如果无法完成转换,将在编译时失败。或者继续在该条件下使用assert,它将在运行时断言。
警告:这不会阻止您将T*
转换为intptr_t
返回U*
使用U而不是T的其他类型。因此,这只能保证您不会使用强制转换如果从<=>转换为<=>并返回<=>,则更改指针的值。 (感谢Nicola指出你可能会期待另一种保护)。
你想要做的事情听起来像是一个非常糟糕和危险的想法,但如果你必须这样做(即你在遗留系统或你知道永远不会改变的硬件上工作),那么我建议包装某种包含两个成员的简单结构中的指针:1)指向对象实例的void指针和一个字符串,枚举或其他类型的唯一标识符,它们将告诉您将原始void *转换为什么。这是我的意思的一个例子(注意:我没有打扰测试这个,因此可能存在语法错误):
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 (...) { ... }
}
dynamic_cast<>
是一个铸仅用于 敞篷车 类型(在多态意义上)。迫使铸的 pointer
来一个 long
(litb正确表明的static_assert,以确保大)有关的所有信息 类型的指针 被丢失。有没有办法实现一个 safe_reinterpret_cast<>
获得指针的背:两者的价值和类型。
为澄清我的意思是:
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;
}
有没有办法 function()
确定这种指针,通过并"向下"铸到适当的类型,除非:
- 长被包裹的一个目的一些信息的类型。
- 类型本身就是编码中被引用的对象。
这两个方案都是丑陋的,应该避免,因为被RTTI代理人。
另外,最好使用size_t而不是long - 我认为这种类型可以确保与地址空间的大小兼容。
一旦你决定将一个指针指向一个长指针,你就会把类型的安全性抛到风中。
dynamic_cast用于演员<!>放大器;下一个派生树。也就是说,从基类指针到派生类指针。如果你有:
class Base
{
};
class Foo : public Base
{
};
class Bar : public Base
{
};
您可以这样使用dynamic_cast ......
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风格的演员表。