この単純な階層について考えてみましょう。
_class Base { public: virtual ~Base() { } };
class Derived : public Base { };
_
_Base* p
_を_Derived*
_にダウンキャストしようとすると、dynamic_cast<Derived*>(p)
を使用できます。 _dynamic_cast
_は、p
のvtableポインターをDerived
オブジェクトのvtableポインターと比較することで機能すると考えていました。
しかし、Derived
から別のクラスを派生させるとどうなるでしょうか。私たちは今持っています:
_class Derived2 : public Derived { };
_
この場合:
_Base* base = new Derived2;
Derived* derived = dynamic_cast<Derived*>(base);
_
_Derived2
_のvtableポインターはDerived
のvtableポインターとは関係ありませんが、ダウンキャストは成功しています。
実際にはどのように機能しますか? _dynamic_cast
_が_Derived2
_がDerived
から派生したものかどうかをどのようにして知ることができますか(Derived
が別のライブラリで宣言されている場合はどうなりますか)?
私amがこれが実際にどのように機能するかについての具体的な詳細を探しています(GCCが望ましいですが、他のものも問題ありません)。この質問はではありませんこの質問 の複製です(実際の動作は指定されていません)。
_
dynamic_cast
_が_Derived2
_がDerived
から派生したものかどうかをどのようにして知ることができますか(Derived
が別のライブラリで宣言されている場合はどうなりますか)?
その答えは驚くほど簡単です。_dynamic_cast
_は、この知識を保持することでこれを知ることができます。
コンパイラがコードを生成するとき、_dynamic_cast
_が後で検索できるある種のテーブルでクラス階層に関するデータを保持します。そのテーブルをvtableポインターにアタッチして、_dynamic_cast
_実装で簡単に検索できます。これらのクラスのtypeid
に必要なデータは、それらとともに保存することもできます。
ライブラリが関係する場合、この種のことは通常、関数の場合と同様に、これらの型情報構造をライブラリで公開する必要があります。たとえば、「関数の場合と同様に、「Undefined reference to 'vtable for XXX'」のようなリンカエラーが発生する可能性があります(そして、男の子は迷惑です!)。
マジック。
冗談だ。これを詳細に調査したい場合、GCCに実装するコードはlibstdc ++の一部であるlibsupc ++にあります。
https://github.com/mirrors/gcc/tree/master/libstdc%2B%2B-v3/libsupc%2B%2B
具体的には、名前にtinfoまたはtype_infoが含まれるすべてのファイルを探します。
または、ここの説明を読んでください。おそらく、はるかにアクセスしやすくなります。
https://itanium-cxx-abi.github.io/cxx-abi/abi.html#rtti
これは、コンパイラーが生成するタイプ情報のフォーマットを詳述し、ランタイムサポートが正しいキャストパスを見つける方法の手がかりを与えるはずです。
Dynamic_castは、Derived2がDerivedから派生したかどうかをどのようにして知ることができますか(Derivedが別のライブラリで宣言された場合はどうなりますか)?
dynamic_cast
自体は何も知りません。その事実を知っているコンパイラです。 vtableには、仮想関数へのポインターのみが含まれているとは限りません。
これが私が(単純に)行う方法です。私のvtableには、dynamic_cast
が使用するいくつかの型情報(RTTI)へのポインタが含まれます。型のRTTIには基本クラスへのポインターが含まれるため、クラス階層を上に移動できます。キャストの疑似コードは次のようになります。
Base* base = new Derived2; //base->vptr[RTTI_index] points to RTTI_of(Derived2)
//dynamic_cast<Derived*>(base):
RTTI* pRTTI = base->vptr[RTTI_index];
while (pRTTI && *pRTTI != RTTI_of(Derived))
{
pRTTI = pRTTI->ParentRTTI;
}
if (pRTTI) return (Derived*)(base);
return NULL;