2016-07-27 26 views
1

は、だから私はのdynamic_cast、C-スタイルキャストstatic_castをの違いについてを少し多くを学ぶためにしようとしていると私は決めた作品Cスタイルキャストの違いを反映するこの例を試してみてください。static_castかなり良いです。メソッドの呼び出しが

class B 
{ 
public: 
    void hi() { cout << "hello" << endl; } 
}; 

class D: public B {}; 

class A {}; 

int main() 
{ 
    A* a = new A(); 
    B* b = (B*)a; 
    b->hi(); 
} 

さて、このコードスニペットは、非常にうまくいかないと悪いキャストが全く検出されないCスタイルキャストことを反映すべきです。部分的にはそれが起こります。悪いキャストは検出されませんが、プログラムがb->hi();でクラッシュする代わりに、画面に "hello"という単語が表示されたときに驚いていました。

これはどうしてですか? Bオブジェクトがインスタンス化されていないときに、そのようなメソッドを呼び出すために使用されたオブジェクトは何ですか?私はg ++を使ってコンパイルしています。

+0

このような無関係な型へのキャスト(C++で 'reinterpret_cast'は効果的に何が行われますか)は未定義の動作です。これは、実装が望みどおりに行うことができることを意味します。 – Yuushi

+0

ヒント:壊れた時計でさえ、1日に2回、適切な時間を示します。 –

+0

Cスタイルのキャストは、コンパイラに 'これはBオブジェクトであると信じている'と伝えるプログラマーのようなものです。コンパイラは黙認する以外に選択肢はありません。実行時に何が起こるかは定義されていません。これはまさにCスタイルのキャストがとても危険な理由です。 –

答えて

3

他の人が言ったように、未定義の動作です。

なぜ機能していますか?コンパイル時に関数呼び出しが静的にリンクされている可能性があります(仮想関数ではありません)。関数B::hi()が存在するので、呼び出されます。 class Bに変数を追加し、それを関数hi()で使用してください。その後、画面上の問題(ゴミ値)が表示されます:

class B 
{ 
public: 
    void hi() { cout << "hello, my value is " << x << endl; } 

private: 
    int x = 5; 
}; 

そうでなければ、あなたが機能hi()が仮想作ることができます。次に、関数はすぐに実行し、プログラムがクラッシュした時に、動的にリンクされています

​​
+1

標準では、いずれかの提案がクラッシュする必要があることを保証するものではありません。 – user2079303

+0

@ user2079303それは本当です。私のコンピュータがそのような呼び出しの後に火星への使命を開始するか、ターミネーター:Genisysが始まることがあります。しかし、彼がgccを使用している場合、結果はまさに私のようなものになる可能性があります:最初の奇妙な値と2番目の例のクラッシュ。 – Lehu

1

これはどうしてですか?

これは発生する可能性があるためです。何でも起れる。振る舞いは、は未定義です。

予期せぬことが起こったという事実は、UBがなぜ危険なのかをよく示しています。それが常にクラッシュを引き起こした場合、対処するほうがはるかに簡単です。

ほとんどの場合、このようなメソッドを呼び出すために使用されたどのようなオブジェクトは、コンパイラは、盲目的にあなたを信頼し、そして(それはない)bはタイプBのオブジェクトにポイントをすることを前提としています。あたかも仮定が真実であるかのように、指摘されたメモリを使用するでしょう。メンバ関数はオブジェクトに属しているメモリにアクセスしませんでした。そして、正しい型のオブジェクトがあった場合と同じように動作しました。

未定義のため、動作がまったく異なる可能性があります。プログラムを再度実行しようとすると、標準では悪魔があなたの鼻から飛び出さないことは保証されません。

1

ためhi()方法自体の実装、および未定義の動作と呼ばれるC++仕様の特異な部分のこの唯一の作品。

互換性のないポインタ型へのCスタイルのキャストを使用したキャストは未定義の動作です。文字通り何かが起こる可能性があります。

この場合、コンパイラはあなたを信頼してくれることになっていると確信しており、実際にはのインスタンスへの有効なポインタであると確信することになりました。ランタイム動作を伴わないためです。あなたはそれにhi()を呼び出すとするので、この方法では動作します:それはBなくAに属するすべてのインスタンス変数にアクセスしない

  • (確かに、それはまったくのインスタンス変数にアクセスしない)
  • それはですそれは

したがって、それは動作しますが、ほとんどすべての非自明な例では、メソッド呼び出しに続いて、このような不正なキャストがつながると呼ばれることがvtableのb年代で検索する必要はありませんので、仮想ではありませんクラッシュやメモリの破損でそして、あなたはこの種の振る舞いに頼ることはできません - 未定義は、実行するたびに同じでなければならないということを意味しません。コンパイラは、このコードで乱数ジェネレータを挿入し、1を生成すると元のDoomの完全なコピーを起動することができます。明白ではないかもしれないし、それをそのように扱う必要があるので、未定義の振る舞いを含むものが動作するように見えるときはいつも、そのことを念頭に置いてください。

関連する問題