C ++の継承メカニズムが不透明なのはなぜですか?
-
06-07-2019 - |
質問
たとえば、なぜvtableを調べるための言語サポートがないのですか?メンバー関数を新しいものに置き換えられないのはなぜですか?そのような機能を有効に活用する方法があると直感しています。
そのようなことを可能にする他の言語はありますか?
解決
主な理由は、vtableを実装の詳細として保持することで、具体的な実装で適切と思われるように最適化できるためです。これは、例えば特定のメソッド(またはすべてのメソッド)の仮想呼び出しがないことを証明できる場合は、vtableをトリムまたは完全に削除します。または、vtableディスパッチをif-elseタイプチェックに置き換えます。いくつかの選択肢しかないことがわかります(この場合、分岐予測は機能しますが、vtablesでは機能しないため、if-else分岐をインライン化できるため、これは有利です)。 vtable内のメソッドを並べ替えて、最も一般的に呼び出されるメソッドを先に配置したり、一般的に呼び出されるメソッドがvtable内の隣接スロットをすぐに埋めてキャッシングを利用したりできます。などなど。もちろん、これらのすべての実装は、vtableレイアウトを完全に予測不可能にし、(言語仕様により)実装に公開される場合は役に立たなくなります。
同様に、vtablesは見た目ほど単純ではありません。たとえば、コンパイラは、多くの場合、仮想継承、または共変の戻り値型と組み合わせた多重継承などのために、サンクを生成して this
ポインターを修正する必要があります。これもまた、「単一の最良の方法」を持たないものです。それを行うには(これが異なるコンパイラが異なる方法で行う理由です)、それを標準化するには、特定の方法で効果的に解決する必要があります。
つまり、「vtable switching」上位レベルの構造として公開されている場合、潜在的に有用な手法です(そのため、最適化が可能です)。例については、UnrealScriptを参照してください。これにより、クラスの複数の状態を定義できます(1つはデフォルト、もう1つは名前付き)、名前付き状態の一部のメソッドをオーバーライドします。派生クラスは、既存の状態のメソッドをさらにオーバーライドしたり、独自の状態を追加してオーバーライドしたりできます。さらに、状態は他の状態を拡張できます(したがって、メソッドが特定の状態でオーバーライドされない場合、「親」状態にフォールバックし、チェーンがデフォルト状態に達するまで続きます)。アクターモデリング(ゲームは本質的に)にとって、これは非常に理にかなっているので、UnrealScriptにはそれがあります。そして、これらすべての明白な効率的な実装メカニズムは、vtableの切り替えであり、各状態には個別のvtableがあります。
他のヒント
これはコンパイラの実装の詳細だからです。その実装は変更される可能性があり、それに依存するコードは脆弱性が高くなります。
C ++は、使用しないものを「支払う」ことのない言語です。この種のランタイムサポートは、その哲学に反して実行されます。
それをサポートする多くの言語があります(スペクトルのより動的な終わりに)。
通常はそうですが、 VTable
として実装する必要はありません。つまり、C ++には VTable
のようなものはありません!
JavaScript、Python、およびRubyはすべてこれを実行できます。これらの言語では、クラス定義とインスタンス定義は実行時に変更可能です。抽象的には、各オブジェクトと型はメンバー変数とメソッドのディクショナリであり、それらを調査および更新できます。
これは、生成されたバイナリコードを書き換えることが必要になるため、C ++では不可能です。これには、かなりのパフォーマンスコストがかかります。
Vtablesは、一部のコンパイラの特定の状況にのみ存在します(つまり、標準では指定されていませんが、実装の詳細です)。それらが存在する場合でも、仮想関数があり、多態性を実装するために間接性が必要な場合にのみ発生します。これが不要な場合は、それらを最適化して、呼び出し時のインダイレクションのオーバーヘッドを節約できます。
残念ながら(または、問題に関するあなたの見解に応じて;-)、C ++はモンキーパッチをサポートするようには設計されていません。場合によっては(例:COM)、vtableは実装の一部であり、舞台裏について話すことができるかもしれません。ただし、これはサポートされることも移植されることもありません。
Pythonのような動的言語でそのようなことができると信じています:
>>> class X():
... def bar(self): print "bar"
...
>>> x = X()
>>> x.bar()
bar
>>> def foo(x): print "foo"
...
>>> X.bar = foo
>>> x.bar()
foo
C ++のような静的言語との違いは、インタープリターが実行時にすべての名前を検索し、次に何をすべきかを決定することです。
C ++には、「メンバー関数を別のメンバー関数に置き換える」ための他のソリューションがありそうです。問題、最も単純なものは関数ポインタを使用している可能性があります:
#include <iostream>
class X;
typedef void (*foo_func)(const X&);
void foo(const X&) { std::cout << "foo\n"; }
void bar(const X&) { std::cout << "bar\n"; }
class X
{
foo_func f;
public:
X(): f(foo) {}
void foobar() { f(*this); }
void switch_function(foo_func new_foo) { f = new_foo; }
};
int main()
{
X x;
x.foobar();
x.switch_function(bar);
x.foobar();
}
(fooとbarはX&amp;引数を使用しません。この例では、Pythonの例と同様です)
私はvtableを公開する静的にコンパイルされた言語に取り組んでいますが、髪を露出します。
- C ++でできることは何でも、まっすぐなCで少しエルボグリースを塗ることができます。
- 控えめで妥当なCプログラムはすべてC ++でコンパイルする必要があります。
おそらく、C ++の組み込み機能を使用せずに独自のvtableを実装することを望みます。メンバー関数へのポインター(ptmf)を使用すると、多くの楽しみがあります!
需要がほとんどなく、実装が容易ではないため、vtableイントロスペクションでコンパイルされた言語を見つけるのに苦労します。通訳言語の場合、状況は逆です。
他の言語はありますか そこに私はそのようなことをすることができます 物事?
Objective-C(およびObjective-C ++)を使用すると、コンパイル済みのメソッドをランタイムで置き換えることができます。静的および動的な手法の最適な組み合わせか、求める人に応じて最悪の手法のいずれかです。
他の人が指摘したように、「vtable」という概念はありません。 C ++標準では、名前マングリングによく似た、ほぼ普遍的な実装手法にすぎません。
コンパイルされた言語でその場で関数を再定義できることを探しているなら、Common Lispに興味があるかもしれません。他にも存在しなければなりませんが、私が考えることができる他の言語は、静的な継承と機能を持つか、パフォーマンスが大幅に低下して解釈されるかのいずれかです。