オブジェクトをサブクラスにダウンキャストして、追加の変数や仮想メソッドを定義しないことは可能ですか?
これらのクラスがあれば、
class A { public: A (); };
class B : public A { public: void method1 () {} B (); };
これは(1)可能で、(2)標準で安全ですか?
A* a = new A ();
B* b = (B*)a;
b->method1();
ポインター変換は として機能しstatic_cast
ます。5.2.9/2 によると、
タイプ [
A
] のオブジェクトが実際にはタイプ [ ] のオブジェクトのサブオブジェクトである場合B
、結果はタイプ [ ] の外側のオブジェクトを参照しますB
。それ以外の場合、キャストの結果は未定義です。
オブジェクトはオブジェクトのサブオブジェクトではないB
ため、結果は未定義です。
場合でもreinterpret_cast
、結果のポインターを介してオブジェクトの値にアクセスすると、厳密なエイリアシングに違反するため、未定義の動作になります。C++11 標準では、3.10/10:
プログラムが、次の型以外の glvalue を介してオブジェクトの格納された値にアクセスしようとした場合、動作は未定義です。
「データ メンバーまたは仮想メンバー関数を追加しないオブジェクトの動的型の派生クラス」は、次のリストにありません。
実際の動作によってmethod1
は、オブジェクトを呼び出すときにオブジェクトの保存された値にアクセスすることを回避できる場合があります。しかし、それが可能であるとは確信していません。標準の他の場所で特に明記されていない限り、関数が実際にデータメンバーを使用していなくても、非静的メンバー関数を呼び出すと本質的に「オブジェクトの格納された値にアクセスする」と安全のために仮定します。
これは、常に、またはほぼ常に実際に機能する厄介なケースの 1 つです。しかし、それが保証されているわけではないので、動作しているように見え、出力されたコードが問題ないように見えたとしても、いつの日か新しい最適化によってそれが壊れてしまうのではないかと恐れることになります。
定義されると、C++ クラスは、新しいメンバー関数を含む新しいメンバーに対して閉じられます。したがって、で作成されたオブジェクトにnew A()
は、これまでにないすべてのメンバー関数があります。非メンバー関数を作成するだけです。メンバーがない限りA
、メンバー関数protected
とまったく同じアクセスが可能A
です。メンバーが存在する場合A
は、そこから派生する承認済みの方法があり、これを使用して の適切なインスタンスを作成する必要があります。protected
B
メンバー関数の構文があなたにとってそれほど意味がある場合、クラスによっては次のA
ように記述できる場合があります。
B b = *a; // "copy" the object (give B a suitable ctor)
b.method1(); // act on it
*a = b; // copy it back (A needs copy assignment operator)
明らかに、ここには動作を停止する可能性のある問題があります。まず、オブジェクトがコピー可能かどうか、スレッドセーフかどうか、およびオブジェクトが破棄されるとすぐにぶら下がり始めるmethod1
ポインター/参照を格納するかどうかです。C++11 では、コピーはおそらく効率のための移動になる可能性がありますが、それでも、メンバー関数構文を使用するためにジャンプしなければならないフープは価値がないことに同意してくれることを願っています。b
b
これは、C ++で行うのが最善ではない、Cvoid*キャストに似ています。しかし、私はこれを何度も行いました、そしてそれはうまくいきます。
B* b = static_cast< B* >a または、試みたのは安全でないダウンキャストであり、基本クラス オブジェクト (A) のアドレスを派生クラス (B) ポインターに割り当てます。したがって、そのポインターを介して何かにアクセスすると、未定義の動作が発生します。
A のインスタンスの 1 つの可能なオブジェクト レイアウトを想定してみましょう。
a ->|vptr|
強制キャストを実行すると:
b ->|vptr|
a は B のインスタンスまたは B のサブクラスを指していないため、これは間違いなく安全ではありません。仮想メソッドを呼び出すか、フィールドを変更すると (この場合ではありません)、一般的に未定義の動作が発生したり、このレイアウトでエラーが発生したりします。
ただし、メソッド 1 は非仮想であるため、仮想テーブルを検索する必要はありません。method1 の実装は「これ」に対して何もできず、何もできないため、この仮想オブジェクトレイアウトでコードを実行すると、おそらくエラーは報告されません (James のコメントを参照)。
この非常によく書かれた投稿を読む必要があります。
通常のキャスト vs. static_cast vs. dynamic_cast
「static_cast」を使用する場合は、「強制」変換であり、これは本質的に安全ではないことに注意してください。