web-dev-qa-db-ja.com

dynamic_castの実装方法

この単純な階層について考えてみましょう。

_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が望ましいですが、他のものも問題ありません)。この質問はではありませんこの質問 の複製です(実際の動作は指定されていません)。

37
Avidan Borisov

_dynamic_cast_が_Derived2_がDerivedから派生したものかどうかをどのようにして知ることができますか(Derivedが別のライブラリで宣言されている場合はどうなりますか)?

その答えは驚くほど簡単です。_dynamic_cast_は、この知識を保持することでこれを知ることができます。

コンパイラがコードを生成するとき、_dynamic_cast_が後で検索できるある種のテーブルでクラス階層に関するデータを保持します。そのテーブルをvtableポインターにアタッチして、_dynamic_cast_実装で簡単に検索できます。これらのクラスのtypeidに必要なデータは、それらとともに保存することもできます。

ライブラリが関係する場合、この種のことは通常、関数の場合と同様に、これらの型情報構造をライブラリで公開する必要があります。たとえば、「関数の場合と同様に、「Undefined reference to 'vtable for XXX'」のようなリンカエラーが発生する可能性があります(そして、男の子は迷惑です!)。

25

マジック。

冗談だ。これを詳細に調査したい場合、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

これは、コンパイラーが生成するタイプ情報のフォーマットを詳述し、ランタイムサポートが正しいキャストパスを見つける方法の手がかりを与えるはずです。

14
Sebastian Redl

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;
2
Arne Mertz