型キャスト。 static_castとdynamic_castの視覚的な違い

良い一日。 インターネットには、型変換演算子の違いに関する記事がたくさんありますが、実際にはこのトピックに理解を加えることはありませんでした。 自分で理解しなければなりませんでした。 かなりわかりやすい例で私の経験を共有したいと思います。



この記事は、C ++での型変換を理解したい人を対象としています。



したがって、次の継承階層があります。



#include <iostream> struct A{ A():a(0), b(0){} int a; int b; }; struct B : A{ B():g(0){} int g; }; struct D{ D():f(0){} float f; }; struct C : A, D{ C():d(0){} double d; };
      
      





この図は、継承の階層とメモリ内の相続人のデータメンバーの場所を示しています。



画像



小さな余談:なぜ型変換がそれほど重要なのですか? 労働者と農民の観点から言えば、タイプXのオブジェクトをタイプYのオブジェクトに割り当てる場合、タイプXのオブジェクトが割り当てた後の値を決定する必要があります。



static_castを使用して始めましょう:



 int main(){ C* pC = new C; A* pA = pC; D* pD = static_cast<D*> (pC); std::cout << p << " " << pD << " " << pA << std::endl; return 0; }
      
      





ポインター値を出力するとき、なぜこれが効果なのですか(ポインター値は変数があるアドレスです)? 実際、static_castはポインターシフトを生成します。

例を考えてみましょう:



 D* pD = static_cast<D*> (pC);
      
      





1. C *からD *への型変換が発生します。 この結果は、タイプD *(tempDと呼びます)のポインターであり、クラスDから継承されたクラスCのオブジェクト内の部分を指します(pC自体の値は変更されません!)。



2.ここで、ポインターpDにtempDポインターの値を割り当てます(すべてが適切で、タイプは同じです)。

合理的な質問:なぜ実際にポインターを移動する必要があるのですか? 簡単に言えば、クラスD *へのポインターはクラスDの定義によって導かれます。バイアスがない場合、ポインターDを介して変数の値を変更すると、クラスDから継承した変数に属さないクラスCのオブジェクトの変数を変更します(ポインターpD pCと同じ意味を持ち、pD-> fが逆になったとき、実際に変数を操作します

a)。



結論:static_castは、クラス階層で作業する場合、ポインターを介してクラス変数にアクセスできるようにポインターの値を決定します。



static_castの欠点について話しましょう。 同じ継承の階層に戻りましょう。



次のコードを検討してください。



 int main(){ C* pC = new C; A* pA = static_cast<A*>(pC); D* pD = static_cast<D*> (pC); B* pB = static_cast<B*> (pA); std::cout << &(pB->g) << " " << pD << " " << pA << std::endl; pB->g = 100; std::cout << pC->a << " " << pC->b << " " << pC->f << std::endl; return 0; }
      
      





pC-> fが0以外の値を持っているのはなぜですか? コードを1行ずつ検討します。



  1. ヒープでは、メモリはタイプCポインタの下に割り当てられます。
  2. アップコンバージョンが行われます。 ポインタpAはpCと同じ意味を持ちます。
  3. アップコンバージョンが行われます。 ポインターpDの値は、ポインターpCが指すクラスCのオブジェクト内の変数fのADDRESSです。
  4. ダウンコンバージョンがあります。 ポインターpBは、ポインターpAと同じ意味を持ちます。


危険はどこですか? 実際、この実施形態では、pBポインターは、pAが指すオブジェクトがタイプBのオブジェクトであると本当に信じていました。 はクラスA)の継承者ですが、pAが指すオブジェクトが実際にタイプBのオブジェクトであることを検証しません。



危険そのもの:



画像



ポインターpBを介して変数gに書き込みたい場合(pBはB型のオブジェクトを指していることを完全に確信しているため)、実際にデータをクラスDから継承した変数fに書き込みます。さらに、ポインターpDは変数に書き込まれた情報を解釈しますf、フロートのように、coutを介して出力するときに表示されます。



そのような問題を解決する方法は?

これを行うには、dynamic_castを使用します。これは、クラス階層の有効性だけでなく、ポインターがキャストする型のオブジェクトを指していることも確認します。



このようなチェックを可能にするには、クラスに仮想性を追加します(dynamic_castは仮想関数テーブルを使用してチェックを行います)。



同じクラス階層での問題の解決策のデモンストレーション:



 int main(){ C* pC = new C; A* pA = pC; if(D* pD = dynamic_cast<D*> (pC)) std::cout << " OK " << std::endl; else std::cout << " not OK " << std::endl; if(B* pB = dynamic_cast<B*> (pA)) std::cout << " OK " << std::endl; else std::cout << " not OK " << std::endl; return 0; }
      
      





コードを実行し、操作を確認することをお勧めします



 B* pB = dynamic_cast<B*> (pA)
      
      





それは機能しません(pAがdynamic_castをチェックして判定をレンダリングしたタイプCのオブジェクトを指しているため)。



リンクは提供していません。ソースは個人的な経験です。



みんなありがとう!



All Articles