1

对于一个系统,我需要将指针转换为 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

dynamic_cast 运算符在运行时执行类型转换。dynamic_cast 运算符保证将指向基类的指针转换为指向派生类的指针,或将引用基类的左值转换为对派生类的引用。因此,程序可以安全地使用类层次结构。此运算符和 typeid 运算符在 C++ 中提供运行时类型信息 (RTTI) 支持。

如果它为空,我想得到一个错误,所以我编写了自己的动态演员表

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,但是当我想将派生的类型转换为它的基时会遇到问题。那么我该如何解决呢?

4

9 回答 9

2

dynamic_cast只能在通过继承相关的类之间使用。要将指针转换为 long 或反之,您可以使用reinterpret_cast. 要检查指针是否为空,您可以assert(ptr != 0). 但是,通常不建议使用reinterpret_cast. 为什么需要将指针转换为long?

另一种选择是使用联合:

union  U { 
int* i_ptr_;
long l;
}

同样,也很少需要联合。

于 2008-11-22T08:53:46.170 回答
1

请记住,在 Windows 64 中,指针将是 64 位数量,但long仍将是 32 位数量,并且您的代码已损坏。至少,您需要根据平台选择整数类型。我不知道 MSVC 是否支持uintptr_tC99 中提供的用于保存指针的类型;如果可用,那将是最好的类型。

至于其余的,其他人已经充分解决了dynamic_castvs的原因和原因reinterpret_cast

于 2008-11-22T13:14:28.017 回答
1

在以仅支持 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;
}
于 2008-11-27T22:20:19.677 回答
0

dynamic_cast<>是一个旨在仅用于可转换类型(在多态意义上)的强制转换。强制将 a 强制pointer转换为 a long(litb 正确建议 static_assert 以确保大小的兼容性)有关指针类型的所有信息都将丢失。没有办法实现 asafe_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()确定传递的指针类型并将其“向下”转换为正确的类型,除非:

  • long 被一个带有一些类型信息的对象包装。
  • 类型本身被编码在引用的对象中。

这两种解决方案都很丑陋,应该避免,因为是 RTTI 代理。

于 2008-11-22T08:56:32.530 回答
0

您可以使用reinterpret_cast转换为整数类型并返回到指针类型。如果整数类型足够大以存储指针值,则该转换不会更改指针值。

正如其他人已经说过的那样,在非多态类上使用 dynamic_cast 不是定义的行为(除非你进行向上转换,这无论如何都是隐式的,在这里被忽略),它也只适用于指针或引用。不适用于整数类型。

你最好使用::intptr_t在各种 posix 系统上找到的。您可以使用该类型作为您转换为的中间类型。

关于您检查转换是否成功,您可以使用 sizeof:

BOOST_STATIC_ASSERT(sizeof(T1) >= sizeof(T2));

如果无法完成转换,将在编译时失败。或者继续在该条件下使用断言,它将改为在运行时断言。

警告:这不会阻止您使用 U 转换T*intptr_tU*T 以外的其他类型。因此,这只保证您在从T*tointptr_t和 back to转换时不会更改指针的值T*。(感谢 Nicola 指出您可能会期待另一种保护)。

于 2008-11-22T10:32:41.713 回答
0

reinterpret_cast 是在这里使用的正确演员表。

这几乎是它唯一可以安全地做的事情。

reinterpret_cast 从指针类型到类型 T 并返回到原始指针类型会产生原始指针。(假设 T 是至少与原始指针类型一样大的指针或整数类型)

请注意,从指向 T 的指针类型的 reinterpret_cast 是未指定的。没有关于 T 类型的值的保证,除了如果你然后 reinterpret_cast 它回​​到原来的类型,你会得到原来的值。因此,假设您不尝试对您的情况下的中间 long 值做任何事情, reinterpret_cast 是完全安全且可移植的。

编辑:当然,如果您在第二次演员表中不知道原始类型是什么,这将无济于事。在这种情况下,你就完蛋了。long 不可能以任何方式携带关于它是从哪个指针转换而来的类型信息。

于 2008-11-22T12:26:31.763 回答
0

你想做的事情听起来像是一个非常糟糕和危险的想法,但如果你必须这样做(即你在遗留系统或你知道永远不会改变的硬件上工作),那么我建议将指针包装在一些一种包含两个成员的简单结构: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 (...) { ... }

}
于 2008-11-22T20:48:46.757 回答
0

另外,最好使用 size_t 而不是 long - 我认为这种类型可以确保与地址空间的大小兼容。

于 2008-12-28T14:01:22.317 回答
-1

一旦您决定将指针投射到 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 风格的演员表。

于 2008-11-22T17:38:43.460 回答