2

この疑いを持ったとき、私は C++ オブジェクトについて読んでいました。2つのクラスがあるとします

class X
{
     virtual int def() { };
}

class Y
{
     virtual int abc() { };
}
class Z : public X,public Y
{
     virutal int abc() { return a };
     int a;
}

今私が理解しているのは、Y と Z の両方が Z::abc() への参照を仮想テーブルに持っていることで、呼び出す正しい関数を解決するのに役立ちます。検討

Y *y = new Z;
Z *z = new Z;
y->abc() // I understand this is done by reaching the vptr of y by this = this + sizeof (X)
and z->abc() // z has its own vptr reachable

私の理解では、どちらの場合も「this」ポインターが渡され、呼び出す正しい abc() を見つけた後、プログラムはどのようにして int 値「a」に到達するのでしょうか?ここに画像の説明を入力

渡されたオブジェクトの型に基づいて、コンパイラはどのように "int a" のアドレスを正しく計算しますか?

4

3 に答える 3

3

これはすべて実装の詳細であり、異なるコンパイラは異なることを行います。thisこの問題には 2 つの一般的なアプローチがありますが、どちらも関数定義でポインターが参照するものを修正することに依存しています。

1 つのアプローチは、thisポインターが常に完全なオブジェクトを参照するようにすることです (少なくとも、その仮想関数の最終オーバーライドのレベルで)。このアプローチでは、多重継承が存在する場合、vtable のエントリには実際の関数へのポインターは含まれませんが、ポインターを調整thisしてオーバーライダーにジャンプするトランポリンへのポインターが含まれます。このアプローチの利点は、左端の空でないベースと単一継承のすべてのケースで、vtable 内のエントリが実際にはオーバーライドであり、関連するコストがないことです。これは、Linux を含むさまざまな OS で使用される Itanium C++ ABI で採用されているアプローチです。

もう 1 つの方法は、メンバー関数を最初に宣言したサブオブジェクトのアドレスを常に渡すことです。この場合、呼び出し元は、vtable をジャンプするthisにサブオブジェクトを参照するようにポインターを調整します。最初のケースと同様に、オフセットがない場合 (最初の非空ベース、単一継承)、コンパイラは調整を追加する必要がなく、コストも発生しません。最終オーバーライドの実装内では、コンパイラは完全なオブジェクトからのオフセットではなく、関数を宣言したベースのポインターからのオフセットを使用します。これは Windows/Visual Studio の場合だと思いますが、これを確認するための VS C++ ABI にアクセスできないため、ここで私の言葉を信じないでください。

3 番目の方法は、vtable に調整を直接格納することです。このアプローチはあまり一般的ではなく、(0 を追加して) 更新する必要がない場合でも、オフセットを調整するコストが発生します。このアプローチを使用する現在のコンパイラは知りません。

于 2013-10-03T02:34:23.667 に答える
1

あなたは治療することができます

 virtual int abc() { return a };

なので

 virtual int abc(Z * const this) { return this->a };

したがって、すべての非静的メンバー関数にはhidden、オブジェクトを指すポインターがあります。したがって、コンパイラが仮想メソッドを見つけることができる限り、どこにあるかがわかりますa

于 2013-10-03T02:23:41.377 に答える
0

John の指摘に基づいて構築すると、派生クラスが基底クラスのメソッドをオーバーライドする場合、コンパイラは派生クラスの vtable への vpointer と呼ばれるものを自動的に作成します。派生クラスのthis背後にあるポインターは、派生クラスの vtable を指す vpointer を指します。これがポリモーフィズムの仕組みです。

于 2013-10-03T02:33:34.173 に答える