4

重複の可能性:
通常のキャストvs. static_cast vs. dynamic_cast
未定義、未指定、および実装定義の動作

私は奇妙な問題に直面しています。次のスニペットでは、クラスを定義します

   class NewClass
   {
      public:
         void Test()
         {
             cout<<"NewClass Test"<<endl;
         }
   };

私のmain()メソッドでは、次のように記述します。

        void main()
        {
           int *ptr = new int();
           NewClass *n = ((NewClass *)ptr);
           n->Test();
        }

「NewClassTest」と表示されます。NewClassへのポインタを型キャストし、それを機能させる方法がわかりません。

前もって感謝します!

4

4 に答える 4

3

これは、静的なディスパッチです。thisこの場合、実際には不要です(たとえば、内で使用または依存されていませんNewClass::Test())。

アドレスによる型変換はそのままキャストしNewClass *n = ((NewClass *)ptr);、このコンテキストでは型チェックはありません。NewClassつまり、どこにも新しいインスタンスを作成するのではなく、で指定さint*れたアドレスのメモリを単に処理しているだけNewClass*です。これは危険な変換であり、避ける必要があります。型安全性が失われているアドレス(たとえばvoid*)を介してオブジェクトをファネルする必要がある場合は、常に両端が送受信内容を認識していることを確認してください。幸いなことに、型安全性の消去はあまり一般的ではなくなっています。

結果は未定義ですが、ほとんどの場合、悪い副作用が予想され、データをそのように再解釈することは絶対に避けてください。

この場合、コンパイラーは結果を知っているため、結果を挿入した可能性があります。さらに、この場合、オブジェクトのアドレスまたは状態に実際に依存しないため、エラーは表示されTest()ませんでした。の状態/データ/メンバー/動的メソッド/vtableに依存しませんthis

std::stringsなどのメンバーを追加しNewClassてそれらも印刷する場合...現在よりも早く爆発することが期待できます:)

危険性が明らかでない場合:これは非常に危険な変換です-によって裏付けられたすべてのデータint*はとして再解釈されNewClass*、その内部メモリと構造(vtablesやマジッククッキーなど)はそれに応じて再解釈されます。int*割り当て( )の終わりを超えて読み取るか、完全に無関係な型として扱うことによって、プログラムのセグメンテーション違反が発生するまでにそれほど時間はかかりません。intこの場合、vtableまたはにいくつかstd::stringのsを追加NewClassしたり、それらのメンバーからの読み取りやそれらのメンバーへの書き込みなどのデータ。

于 2012-05-01T06:33:28.247 に答える
1

なぜそれが機能しないのかという複雑なことを考え始める前に、それを想像するのに役立つ簡単なシナリオを検討してください。

クラスは、メソッドがアタッチされたデータ構造です。もちろん、コンパイラはこれまでとは異なるため、動作は未定義と見なすことができますが、当面はこれを無視してください。

空のデータ構造(つまりデータがない)がありますが、それに接続されているメソッド(Test())がまだあります。

したがって、何かへのポインタを宣言するとき(あなたの注意でint)、ポインタはあるメモリを指しているだけです。これで新しいInt()ができたので、ptrが指すメモリは整数サイズになります。

クラスにはデータがなく、メモリ内のオブジェクトを特定の方法(仮想メソッドなど)でメモリ内に配置する必要がある内部構造がないため、何かを指している、または実際には何も指していないと見なすことができます。したがって、メソッドを呼び出すことができます。

このようなクラスを作成して、何が起こるかを確認します。

   class NewClass 
   { 
      private int i;
      public: 
         void Test() 
         { 
             cout<<"NewClass Test i="<< i << endl; 
         } 
   };

    void main() 
    { 
       int *ptr = new int();
       *ptr =  10; 
       NewClass *n = ((NewClass *)ptr); 
       n->Test(); 
    } 

それが何を印刷するかを見てください。

これを理解している場合は、コンパイラがオブジェクトをどのようにレイアウトするかを試してみてください。これにより、この動作がプラットフォームに存在する理由について多くのことがわかります。

于 2012-05-01T06:41:56.153 に答える
0

これは未定義の動作のようです。ただしreinterpret-cast、C ++でいつでもこれを行うことができます。reinterpret_cast演算子の誤用は、簡単に安全ではない可能性があります。目的の変換が本質的に低レベルでない限り、他のキャスト演算子の1つを使用する必要があります。reinterpret_cast演算子は、char*からint*への変換、またはOne_class*からUnrelated_class*への変換など、本質的に安全ではない変換に使用できます。

于 2012-05-01T06:36:06.560 に答える
0

メソッドは仮想として宣言されていません。これは、それへの呼び出しがコンパイラーによって完全に解決されることを意味します。これは、非メソッド関数の場合と同じですが、正式には自分自身である変数で呼び出す必要がある点が異なりNewClassます。

コンパイラはおそらく仮想メソッドテーブルを使用して仮想メソッドへの呼び出しをディスパッチします。メソッドが仮想の場合、VMTの代わりにガベージが発生し、クラッシュが発生し始めます。

とはいえ、動作は未定義であり、どちらの場合でも何かが発生する可能性があることを意味します。

于 2012-05-01T06:38:20.873 に答える