C ++ステートフル仮想ベースの適切な説明はどこにありますか?
JSF C ++コーディング標準を調べて説明を読みましたが、いくつかの追加情報を探していました。
追加の詳細を提供していただきありがとうございます。
C ++ステートフル仮想ベースの適切な説明はどこにありますか?
JSF C ++コーディング標準を調べて説明を読みましたが、いくつかの追加情報を探していました。
追加の詳細を提供していただきありがとうございます。
Stanley Lippman の著書「Inside the C++ Object Model」には、この主題の優れた概説があります (時代遅れではありますが、今でも有効です)。
仮想性とは間接性に関するものです。簡単に始めましょう:
struct Foo { void bar(int, bool) {} } x;
x.bar(12, false);
Foo::bar()
ここで、インスタンスの呼び出しx
はコンパイル時に完全に認識され、静的に解決されます。インスタンス参照と関数引数が与えられる 1 つの固定関数。関数名、呼び出し、完了。今のところ問題ありません。
次に進む:
struct Boo { virtual void bar(char, float) = 0; };
extern Boo * foreign_function();
Boo * p = foreign_function();
p->bar('a', -1.5);
bar()
今回は、呼び出しがどこに行くべきかをコンパイル時に知る方法はありません。これを解決する唯一の方法は、このメンバー関数のすべての可能なオーバーライドを検索し、動的な型に応じて実行時に正しいものを選択できるようにする間接的なレベルを追加することです*p
。今回は、関数名から始めて、実行時にルックアップを実行してから、呼び出しを行います。このパターンは、今でもかなり馴染みのあるものです。
ここでのポイントは、 の動的型が*p
(非仮想) ベースのサブタイプであることを知っていれば十分でBoo
あるため、これを 1 回のルックアップで実装できます (たとえば、のと互換性のあるテーブルへの vtable ポインター)。Boo
)。
次に、大きな魚に進みます。
struct Voo { virtual void doo(double, void *) = 0; };
struct Left : virtual Voo { virtual void doo(double, void *); } };
struct Right : virtual Voo { virtual void doo(double, void *); } };
struct Most : Left, Right { virtual void doo(double, void *); } };
Left * p = /* address of a Most object, say */;
p->doo(0.1, nullptr);
がどこに行くべきかわからないことはすでにわかっておりdoo()
、実行時に調べる必要があります。ただし、単純な 1 ステップの間接化はもはや不可能です。Left
は のサブクラスでVoo
あり、のサブクラスでもありますがRight
、Voo
の実際のVoo
ベース サブオブジェクトは*p
実際には のサブオブジェクトではありません。オブジェクトです)。実装に関して言えば、単一の vtable ポインターは適切ではありません。代わりに、実際のオブジェクトが持っている vpointer が必要です。Left
Right
Most
Left
Right
ここで、おなじみの状況に陥ります。実行時にしか分からない何かを調べる必要があります。そして今回調べる必要があるのは、実際の仮想ベースです。したがって、プロセスは次のようになります。関数名、実行時の仮想ベースの検索、仮想ベースでの仮想関数の検索、および呼び出しの作成。(仮想性の典型的な vtable 実装では、これは通常、「サンク」または「ポインターへのポインター」を介した追加のルックアップで行われます。)
一言で言えば、「仮想」は「実行時に決定される」という意味です。
(これは、コンパイラに実行時コードの生成を強制するものではありません。ディスパッチのターゲットがコンパイル時に明らかにわかっている場合、呼び出しは非仮想化される可能性があります。ただし、プログラムの動作は「あたかも」のようになります。)