2011-01-10 5 views
1
#include <iostream> 
class A 
{ 
public: 
    A() 
    { 
     std::cout << "\n A_Constructor \t" << this <<std::endl; 
    } 
    void A_Method() 
    { 
     std::cout <<"\n A_Method \t" << this <<std::endl; 
    } 
}; 
class B:public A 
{ 
public: 
    B() 
    { 
     std::cout <<"\n B_Constructor \n"; 
    } 
    void B_Method() 
    { 
     std::cout <<"\n B_Method \t" << this <<std::endl; 
    } 
}; 

int main() 
{ 
    A *a_obj = new A; 
    B *b_obj = static_cast<B*> (a_obj); // This isn't safe. 
    b_obj->B_Method();  
    getchar(); 
    return 0; 
} 

OUTPUT:はstatic_cast疑い

A_Constructor 001C4890
B_Method 001C4890

一切実行時のチェックは型変換に関与していないされていないとして、static_castは安全ではありません。しかし、この例では、私が期待していなかったものを得ました。 B::B()への呼び出しがないので、そのメンバーのいずれもb_objによって呼び出されるべきではありません。それにもかかわらず、私は出力を得た。

この単純なケースでは、安全ではないとはいえ、私は成功したかもしれません。私の疑問がある -

  • 私はclass Bメンバ関数にアクセスすることができたかB::B()への呼び出しは、ありませんが。
  • これは安全ではなく、間違っているかもしれない例を挙げてください(私が前に示したものは悪い例として役立つかもしれませんが、さらに良いものもあります)。

私はVisual Studio 2010で、\ Wallオプションを設定しました。

+0

ヘンリー・スペンサー氏は、「コンパイラに嘘をつければ、復讐を得るだろう」と述べています。あなたのキャストを実行することによってコンパイラはあなたを信じるように強制し、あなたはかなり期待される結果を得ます。 –

+0

cast.cpp:31:12:error: 'B'に 'check'という名前のメンバーがありません –

+0

@Ninefingers - ここで編集中に意味のある名前を付けていました。誰もが容易に追うことができます。しかし、1つを変更するのを忘れました。指摘してくれてありがとう。 – Mahesh

答えて

4

これは未定義の動作です。 UBがクラッシュすることがあります。時々それは "働く"ようである。たとえこの場合に悪いことが起こったとしても、あなたはこれをしてはいけないということは間違いありません。

+0

「未定義の行為」という言葉が混乱することがあります。それが言われるときはいつも、「定義された振る舞い」もそれのサブセットであることを意味しますか? – Mahesh

+0

@Mahesh:はい。定義されていない動作とは、何かが起こる可能性があることを意味正しく動作するように見えるプログラムを含める。または全く何も。またはコンパイラのベンダーが来て、野球のバットであなたを頭で殴ってしまいます。スタンダードに関してはすべて有効です。この国際規格が要件を課していない誤ったプログラム構成または誤ったデータの使用時に生じる可能性があるような、「 – Puppy

+0

の「未定義の動作」の動作。この標準が動作の明示的な定義の記述を省略した場合、未定義の動作も期待できます。 –

0

あなたがしようとしているのは未定義の動作なので、何かが起こる可能性があります。これは、(あなたが何も持っていない)任意のデータメンバーにアクセスしようとしないように動作する(おそらく動作する)ようだ。両方のクラスにいくつかのデータメンバーを追加してメソッドからアクセスしようとすると、動作が完全に予測不可能に変わることがわかります。

0

私は、この特定のケースがUBであるとは思わない。まず、あるタイプから別のタイプへポインタをキャストします。仮想/多重継承は含まれていないので、ポインタ調整は行われないので、基本的にポインタ値は変わりません。確かにそれは間違ったタイプのオブジェクトを指していますが、Bメンバにアクセスしていない限り、何かあったとしても誰が気にしますか?そして、ポインタの調整があったとしても、私たちが指しているメモリにアクセスしていなければ、それでも問題はありません。

次に、この例ではBのメソッドを呼び出します。これは仮想ではないため、hidden引数this(考えてB::B_Method(this))を使用した通常の関数呼び出しです。さて、thisは間違ったタイプのオブジェクトを指していますが、再び誰が気にしますか?それが行う唯一のことはそれを印刷することです。これは常に安全なことです。

実際、NULLポインタを使用してメソッドを呼び出すこともできます。メソッドが仮想ではなく、thisが指すものにアクセスしようとしない限り、動作します。かつて私は多くのプログラムで使用されていたライブラリを持っていました。このライブラリにはシングルトンのようなクラスがありましたが、これは明示的に構成する必要がありましたが、実際にプログラムを実行したことはありません。グローバルなので、インスタンスポインタはデフォルトでNULLに初期化されました。クラスにデータメンバーがまったくないので、完全に機能しました。それから、私はいくつかを追加し、すべてのプログラムが突然クラッシュし始めました。理由を見つけたら、本当にうれしかったです。私は存在していないオブジェクトを使っています。

+0

これがなぜほとんどのコンパイラで「機能する」かのデリケートな説明。しかし、はい、UB:9.3.1p1: 'X'型の非静的メンバ関数が' X'型でないオブジェクト、または 'X'から派生した型のオブジェクトに対して呼び出された場合、動作は未定義です。 – aschepler

+0

@aschepler一般に、関数が何をするのか誰も知っていないので、これは本当です。しかし、私はこの特定の関数が、ほとんどのコンパイラだけでなく、想像を絶するコンパイラやハードウェアではうまくいかない理由を想像することはできません。これは、無効なポインタをC関数に渡すことはUBですが、その関数が単にそれを出力したり、それを完全に無視したりする場合ではありません。 –