1

システムの場合、ポインターを long に変換してから long をポインター型に戻す必要があります。ご想像のとおり、これは非常に危険です。私がやりたかったのは、変換を行うために dynamic_cast を使用することです。それらを混合すると、null ポインターが得られます。このページには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) をサポートします。

nullの場合はエラーを取得したいので、独自の動的キャストを作成しました

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 を使用できることはわかっていますが、同じタイプの安全性は保証されません。

タイプキャストが同じタイプかどうかを確認するには、何を使用すればよいですか? 2 つの typeid を比較することはできますが、派生をそのベースに型キャストしたい場合に問題が発生します。では、どうすればこれを解決できますか?

4

9 に答える 9

2

dynamic_cast継承によって関連付けられたクラス間でのみ使用できます。ポインターを long に、またはその逆に変換するには、 を使用できますreinterpret_cast。ポインターが null かどうかを確認するには、次のことができ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ビットの量になり、コードが壊れていることに注意してください。少なくとも、プラットフォームに基づいて整数型を選択する必要があります。uintptr_tMSVCがポインタを保持するためにC99で提供されているタイプをサポートしているかどうかはわかりません。利用可能な場合は、それを使用するのが最適なタイプです。

残りの部分については、他の人が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<>(ポリモーフィックな意味で)変換可能な型でのみ使用されることを意図したキャストです。pointera から aへのキャストを強制すると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()次のいずれかでない限り、渡されたポインターの種類を判別し、それを適切な型に「ダウン」キャストする方法はありません。

  • long は、型のいくつかの情報を持つオブジェクトによってラップされます。
  • タイプ自体は、参照されるオブジェクトでエンコードされます。

RTTI サロゲートであるため、どちらのソリューションも見苦しく、避ける必要があります。

于 2008-11-22T08:56:32.530 に答える
0

reinterpret_cast は、ここで使用する正しいキャストです。

これは、安全に実行できる唯一のことです。

ポインター型から T 型に reinterpret_cast し、元のポインター型に戻すと、元のポインターが生成されます。(T が、元のポインター型と少なくとも同じ大きさのポインターまたは整数型であると仮定します)

ポインター型から T への reinterpret_cast は指定されていないことに注意してください。T 型の値についての保証はありませんが元の型に再解釈_キャストすると、元の値が得られます。したがって、あなたの場合、中間の long 値で何もしようとしないと仮定すると、 reinterpret_cast は完全に安全で移植可能です。

編集: もちろん、2 番目のキャストで元のタイプが何であったかがわからない場合、これは役に立ちません。その場合、あなたはめちゃくちゃです。long は、変換元のポインターに関する型情報を保持することはできません。

于 2008-11-22T12:26:31.763 に答える
0

を使用reinterpret_castして、整数型にキャストし、ポインター型に戻すことができます。整数型がポインター値を格納するのに十分な大きさである場合、その変換はポインター値を変更しません。

他の人がすでに言っているように、非ポリモーフィック クラスで dynamic_cast を使用する動作は定義されておらず (アップキャストを実行する場合を除きます。これはとにかく暗黙的であり、ここでは無視されます)、ポインターまたは参照に対してのみ機能します。整数型ではありません。

::intptr_tさまざまな posix システムで見つかったものを使用することをお勧めします。その型をキャスト先の中間型として使用できます。

変換が成功するかどうかの確認に関しては、sizeof を使用できます。

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

変換できなかった場合、コンパイル時に失敗します。または、その条件でアサートを使用し続けると、代わりに実行時にアサートされます。

警告:これは、 T 以外の型の U を使用して to にキャストT*することを妨げるものではありません。したがって、これは、 から にキャストしてからにキャストした場合に、キャストによってポインターの値が変更されないことを保証するだけです。(Nicola が指摘してくれたおかげで、別の保護が期待できるかもしれません)。intptr_tU*T*intptr_tT*

于 2008-11-22T10:32:41.713 に答える
0

また、long の代わりに size_t を使用することをお勧めします。この型は、アドレス空間のサイズと互換性があることが保証されていると思います。

于 2008-12-28T14:01:22.317 に答える
0

あなたがしたいことは本当に悪い、危険な考えのように聞こえますが、それをしなければならない場合 (つまり、レガシー システムで作業している場合、または決して変更されないことがわかっているハードウェアで作業している場合) は、ポインターをいくつかでラップすることをお勧めします。 2 つのメンバーを含む一種の単純な構造体: 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 に答える
-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 に答える