C++RTTIのdynamic_cast関数
5973 ワード
この記事はC++RTTIの続きで、前にtypeid()オペレータを紹介しました.この記事ではRTTIのもう一つの概念、すなわちdynamic_を紹介します.cast. 比較typeid,dynamic_castは実際のプロジェクトで大量に使用されます.
まず、一言でdynamic_キャストは何に使うんだ?castは2つのクラスの間でタイプ変換を行い、すなわち1つのポインタタイプを別のタイプに変換します.この変換プロセスは実行時刻に変換されるのでdynamic_と呼ばれますcast(これに対してstatic_cast).dynamic_castの使用は、次の2つの条件を満たす必要があります.両クラス間に親子関係がない場合、sourceは多態でなければならない. 親から子への変換であれば、親(source)もマルチステートである必要があります.
そうでない場合、コンパイラはエラーを報告します::41:error:cannot dynamic_cast 'pc' (of type 'class *') to type 'class *' (source type is not polymorphic)
上記の説明を再説明します:dynamic_castの使用にはsourceがマルチステートである必要があります.例外は、サブクラスから親クラスへの変換です.もう一つ角度を変えて、実はdynamic_castの使用はsourceが多態でなければならない.例外的に中性子類から親類への変換は天然の行為であり、dynamic_は必要ないと考えているからだ.castですね、(Parent*)childのようです.コードを見てみましょう
コンパイラが生成したアセンブリコードは次のとおりです.
コンパイラがpa 2をva 1に直接付与し、dynamic_に呼び出さなかったことがわかります.castは、コンパイラが親と子の関係やメンバー構成を明確に知っているため、マルチステートの問題もないため、コンパイラはタイプ変換作業を完了することができ、実際にはdynamic_キャストはstatic_にマッピングされましたcast使用.
使用状況について議論した後、変換の結果を見てみましょう(sourceはいずれも多態であると仮定します): sourceとdestに親子関係がない場合、2つのクラスが同じに定義されていてもNULLになります. 子から親まで、前に言ったように必ず成功します. 親から子へ、これもdynamic_castの最も一般的な状況は、親ポインタが対応するサブクラスタイプを実際に指しているかどうかに依存し、これが実際のサブクラスオブジェクトであるかどうかを示す.
実行結果は次のとおりです.
b 1とb 2はいずれもAのインスタンスであるが、b 2はB 1のインスタンスではないため、b 1は変換に成功し、b 2は成功しないことがわかる.
前にdynamic_について紹介しましたcastの使用シーンではdynamic_について説明しますcastはどのように働いていますか.コード説明の問題:
クラスAはクラスBの親です.生成されたfoo()関数コードを見てみましょう.
まずパラメータpaがNULLであるかどうかを検証し、そうでなければNULLを直接返し、そうでなければC++libライブラリ関数__を呼び出すdynamic_cast.
ライブラリ関数_dynamic_castには4つのパラメータが必要です.
パラメータは次のように宣言されます. v:sourceオブジェクトアドレス、NOT NULL(前のソースコードで見たNULLであればここには入らない)、sourceがマルチステートであるため、sourceオブジェクトの最初のドメインは虚関数テーブルへのポインタである. src:sourceオブジェクトのクラスタイプ dat:destinationオブジェクトのクラスタイプ というパラメータも分かりませんが、srcがdstのベースクラスである場合は-2、srcがdstとコヒーレントでない場合は0です.
この関数の詳細については、C++ABIドキュメントを検索します.たとえば、次のようにします.https://android.googlesource.com/platform/abi/cpp/+/6426040f1be4a844082c9769171ce7f5341a5528/src/dynamic_cast.cc
次に、src(class A)とdst(class B)の2つのパラメータの値定義を見てみましょう.
パラメータsrcのタイプの場合$ZTI 1 A、パラメータdstのタイプの場合$ZTI1B;内容からこの2つのタイプの定義も異なることがわかります $_ZTI 1 Aのタイプは_class_type_info $_ZTI 1 Bのタイプは_si_class_type_info、はい_class_type_infoのサブクラス;サブクラスには、親クラスタイプへのポインタが含まれます:const_class_type_info* __base_type;
参照/usr/include/c+/4.4.4/cxxabi.h
これらのクラスのタイプ情報はすべて、アプリケーションが起動するとmain関数のエントリの前にグローバル変数に登録され、ユーザープログラムがアクセスできるようにします.
最後にdynamic_をまとめますキャストの使用シーン dynamic_castはポインタタイプの変換を実現するために使用されます. dynamic_キャスト変換に成功した場合はターゲットタイプへのポインタを返し、変換に成功しなかった場合はNULLを返します. dynamic_castはdynamic_castはクラスの虚関数テーブルからクラスタイプ情報を取得する必要があります. dynamic_castの最も一般的な使い方は、抽象ベースクラスから特定の実装クラスに変換することです.
親クラスに複数のシードクラスがある場合、現在親クラスを指すポインタがある場合、親クラスを指すポインタが実際にどのシードクラスを指しているのか分かりませんが、dynamic_を使用します.castは、NULLでなければChildサブクラスへのポインタであり、そうでなければそうではないと判断する.
まず、一言でdynamic_キャストは何に使うんだ?castは2つのクラスの間でタイプ変換を行い、すなわち1つのポインタタイプを別のタイプに変換します.この変換プロセスは実行時刻に変換されるのでdynamic_と呼ばれますcast(これに対してstatic_cast).dynamic_castの使用は、次の2つの条件を満たす必要があります.
そうでない場合、コンパイラはエラーを報告します::41:error:cannot dynamic_cast 'pc' (of type 'class *') to type 'class *' (source type is not polymorphic)
上記の説明を再説明します:dynamic_castの使用にはsourceがマルチステートである必要があります.例外は、サブクラスから親クラスへの変換です.もう一つ角度を変えて、実はdynamic_castの使用はsourceが多態でなければならない.例外的に中性子類から親類への変換は天然の行為であり、dynamic_は必要ないと考えているからだ.castですね、(Parent*)childのようです.コードを見てみましょう
#include
#include
class A1 {};
class A2 : public A1 {};
void foo(A2 * pa2) {
A1 * va1 = dynamic_cast(pa2);
}
int main(int argc, char * argv[]) {
A2 * a2 = new A2();
foo(a2);
return 0;
}
コンパイラが生成したアセンブリコードは次のとおりです.
_Z3fooP2A2:
pushq %rbp
movq %rsp, %rbp
movq %rdi, -24(%rbp)
movq -24(%rbp), %rax
movq %rax, -8(%rbp)
leave
ret
コンパイラがpa 2をva 1に直接付与し、dynamic_に呼び出さなかったことがわかります.castは、コンパイラが親と子の関係やメンバー構成を明確に知っているため、マルチステートの問題もないため、コンパイラはタイプ変換作業を完了することができ、実際にはdynamic_キャストはstatic_にマッピングされましたcast使用.
使用状況について議論した後、変換の結果を見てみましょう(sourceはいずれも多態であると仮定します):
#include
#include
class A {
public:
virtual ~A() {}
};
class B1: public A {};
class B2: public A {};
void foo(A * pa) {
B1 * vb1 = dynamic_cast(pa);
printf("%s
", vb1 == NULL ? "NULL" : "NOT NULL");
}
int main(int argc, char * argv[]) {
B1 * b1 = new B1();
B2 * b2 = new B2();
foo(b1);
foo(b2);
return 0;
}
実行結果は次のとおりです.
NOT NULL
NULL
b 1とb 2はいずれもAのインスタンスであるが、b 2はB 1のインスタンスではないため、b 1は変換に成功し、b 2は成功しないことがわかる.
前にdynamic_について紹介しましたcastの使用シーンではdynamic_について説明しますcastはどのように働いていますか.コード説明の問題:
#include
#include
class A {
public:
virtual ~A() {}
};
class B: public A {};
void foo(A * pa) {
B * vb = dynamic_cast(pa);
}
int main(int argc, char * argv[]) {
B * b = new B();
foo(b);
return 0;
}
クラスAはクラスBの親です.生成されたfoo()関数コードを見てみましょう.
_Z3fooP1A:
pushq %rbp
movq %rsp, %rbp
subq $32, %rsp
movq %rdi, -24(%rbp)
movq -24(%rbp), %rax
testq %rax, %rax
jne .L9
movl $0, %eax
jmp .L10
.L9:
movl $0, %ecx
movl $_ZTI1B, %edx
movl $_ZTI1A, %esi
movq %rax, %rdi
call __dynamic_cast
.L10:
movq %rax, -8(%rbp)
leave
ret
まずパラメータpaがNULLであるかどうかを検証し、そうでなければNULLを直接返し、そうでなければC++libライブラリ関数__を呼び出すdynamic_cast.
ライブラリ関数_dynamic_castには4つのパラメータが必要です.
extern "C" void*
__dynamic_cast (const void *v,
const abi::__class_type_info *src,
const abi::__class_type_info *dst,
std::ptrdiff_t src2dst_offset)
パラメータは次のように宣言されます.
この関数の詳細については、C++ABIドキュメントを検索します.たとえば、次のようにします.https://android.googlesource.com/platform/abi/cpp/+/6426040f1be4a844082c9769171ce7f5341a5528/src/dynamic_cast.cc
#define DYNAMIC_CAST_NO_HINT -1
#define DYNAMIC_CAST_NOT_PUBLIC_BASE -2
#define DYNAMIC_CAST_MULTIPLE_PUBLIC_NONVIRTUAL_BASE -3
/* v: source address to be adjusted; nonnull, and since the
* source object is polymorphic, *(void**)v is a virtual pointer.
* src: static type of the source object.
* dst: destination type (the "T" in "dynamic_cast(v)").
* src2dst_offset: a static hint about the location of the
* source subobject with respect to the complete object;
* special negative values are:
* -1: no hint
* -2: src is not a public base of dst
* -3: src is a multiple public base type but never a
* virtual base type
* otherwise, the src type is a unique public nonvirtual
* base type of dst at offset src2dst_offset from the
* origin of dst.
*/
次に、src(class A)とdst(class B)の2つのパラメータの値定義を見てみましょう.
_ZTS1B:
.string "1B"
_ZTS1A:
.string "1A"
_ZTI1B:
.quad _ZTVN10__cxxabiv120__si_class_type_infoE+16
.quad _ZTS1B
.quad _ZTI1A
_ZTI1A:
.quad _ZTVN10__cxxabiv117__class_type_infoE+16
.quad _ZTS1A
パラメータsrcのタイプの場合$ZTI 1 A、パラメータdstのタイプの場合$ZTI1B;内容からこの2つのタイプの定義も異なることがわかります
参照/usr/include/c+/4.4.4/cxxabi.h
これらのクラスのタイプ情報はすべて、アプリケーションが起動するとmain関数のエントリの前にグローバル変数に登録され、ユーザープログラムがアクセスできるようにします.
最後にdynamic_をまとめますキャストの使用シーン
親クラスに複数のシードクラスがある場合、現在親クラスを指すポインタがある場合、親クラスを指すポインタが実際にどのシードクラスを指しているのか分かりませんが、dynamic_を使用します.castは、NULLでなければChildサブクラスへのポインタであり、そうでなければそうではないと判断する.