2

非静的メンバー関数へのポインターを実現するために使用される基本的なメカニズムを理解しようとしています。コンパイラごとに異なる可能性のある詳細を気にせずに、全体像で vtbl (ポリモフィズムの仮想テーブル) がどのように機能するかに似た答えを探しています。

例:

#include <stdio.h>

class A {
public:
    int i;
    int j;
    void foo(void)  {  };
};


int main () 
{
    int A::*ptr_j = &A::j;
    void (A::*ptr_f)(void) = &A::foo;
    printf("Foo::j   pointer to data member %p\r\n", ptr_j);  
    printf("Foo::foo pointer to function member %p\r\n", ptr_f);
}

結果は

Foo::j データメンバーへのポインター0x4

Foo::foo関数メンバーへのポインター0x804844c

「The C++ Programming Language By Stroustrup」より、

メンバーへのポインター...構造体へのオフセットまたは配列へのインデックスに似ています...

Foo::jデータ メンバーの場合、Pointer-To-Memberは多かれ少なかれ と同等であることを理解していoffsetOf(Foo, j)ます。ホスト環境で gcc コンパイラを使用した場合の値は 4 で、4 と一致しoffsetOf(Foo, j)ます。

関数メンバーの場合、戻り値は0x804844cです。そして、これはグローバルデータ領域に属するアドレスです (クラスがロードされる場所は?)

だから私の質問は:

address である「オブジェクト」とは何ですか0x804844c

  1. offsetOf()これは大きなオフセットであるため、単純な にすることはできません。
  2. vbtl はクラスではなく、インスタンス化されたオブジェクトに関連付けられたエンティティであると考えているため、vtbl のアドレス (または vtbl のエントリのアドレス) にすることはできません。
  3. 関数の実装コードがロードされるアドレスにすることはできません。派生オブジェクトを適用すると、同じポインターが多態的に動作する可能性があるためです。

次に、 address のオブジェクトは何ですか? また、演算子orが適用され0x804844cたときにメンバー関数へのポインタを実際の関数アドレスに変換する際のその役割は何ですか?->*.*

4

1 に答える 1

3

%pの変換指定子を使用して、メンバーへのポインター オブジェクトの値を出力することはできませんprintf。それらは必ずしも通常のポインターではありません。%p指定子には値が必要ですvoid *。厳密に言えば、通常の関数ポインタを で移植可能に出力することさえできません。%pこれには、関数ポインタを に変換する必要があるためvoid *です。

メンバーへのポインター型は、アドレスを保持できる 1 つのマシン ワードに収まるとは限りません。これらは、複数のフィールドを持つデータ構造にすることができます。

vbtl は、クラスではなく、インスタンス化されたオブジェクトに関連付けられたエンティティであると思います。

これは正しくありません。仮想テーブルは静的構造であり、インスタンスは仮想テーブルのみを指します。特定のクラスのすべてのインスタンスは、同じテーブルへのポインターを共有します。

非静的メンバー関数へのポインターは、クラス インスタンス内の任意のデータ メンバーのオフセットを処理する必要がないという点で、1 つの点で単純です。ただし、非静的メンバー関数へのポインターは、仮想関数をサポートする必要があることと、このようなポインターを介して呼び出すコードが仮想関数と非仮想関数のどちらを呼び出しているかを認識または気にしないという事実によって複雑になります。 .

メンバー関数へのポインターの実装戦略の 1 つは、サンク (呼び出しを正しい方法で行うための適切な種類のロジックを実行する、コンパイラによって生成されたコードの断片) を含みます。メンバーへのポインターは、サンクを指すことができます。サンクは、thisオブジェクト内の vtable ポインターを見つける場所と、vtable 内のどのオフセットが最終的に呼び出される関数かを認識しています。

于 2013-07-24T21:16:39.057 に答える