2

疑問があります: クラスメンバー関数へのポインターを宣言できます

void (*MyClass::myFunc)(void);

クラスメンバー変数へのポインターを宣言できます

int (MyClass::*var);

私の質問は、オブジェクト (メンバー関数とメンバー変数で構成される) がメモリ内 (asm レベル) でどのように構造化されているかです。

ポリモーフィズムとランタイム仮想関数を除いて、オブジェクトがなくてもメンバー関数へのポインターを宣言できます。これは、コード関数が複数のクラス間で共有されていることを意味するためです (動作するには *this ポインターが必要ですが)。ちゃんと)

しかし、変数はどうですか?オブジェクト インスタンスがなくてもメンバ変数へのポインタを宣言できるのはなぜですか? もちろん、それを使用するには 1 つ必要ですが、オブジェクトなしでポインターを宣言できるという事実は、メモリ内のクラス オブジェクトが他のメモリ領域へのポインターを使用してその変数を表していると思わせます。

疑問を適切に説明したかどうかはわかりません。もしそうでない場合は、お知らせください。より適切に説明できるように努めます

4

1 に答える 1

4

クラスは非常に単純にメモリに格納されます-構造体とほぼ同じ方法です。クラスインスタンスが格納されている場所のメモリを調べると、そのフィールドが単純に次々にパックされていることがわかります。

ただし、クラスに仮想メソッドがある場合は違いがあります。このような場合、クラスインスタンスに最初に格納されるのは、仮想メソッドテーブルへのポインタです。これにより、仮想メソッドが正しく機能します。これについてはインターネットでもっと読むことができます。これはもう少し高度なトピックです。幸いなことに、それについて心配する必要はありません。コンパイラがすべてを自動的に実行します(つまり、VMTを処理し、心配する必要はありません)。

メソッドに行きましょう。あなたが見るとき:

void MyClass::myFunc(int i, int j) { }

実際、コンパイラはそれを次のようなものに変換します。

void myFunc(MyClass * this, int i, int j) { }

そして、あなたが電話するとき:

myClassInstance->myFunc(1, 2);

コンパイラは次のコードを生成します。

myFunc(myClassInstance, 1, 2);

これは単純化であることに注意してください。これよりも少し複雑な場合もありますが(特に仮想メソッド呼び出しについて説明する場合)、コンパイラによるクラスの処理方法が多かれ少なかれ示されています。WinDbgなどの低レベルのデバッガーを使用する場合は、メソッド呼び出しのパラメーターを調べることができます。最初のパラメーターは通常、メソッドを呼び出したクラスインスタンスへのポインターであることがわかります。

現在、同じタイプのすべてのクラスは、メソッドのバイナリ(コンパイルされたコード)を共有しています。したがって、クラスインスタンスごとにそれらのコピーを作成する意味はありません。したがって、メモリに保持されるコピーは1つだけであり、すべてのインスタンスがそれを使用します。クラスのインスタンスがない場合でも、なぜメソッドへのポインタを取得できるのかが明確になっているはずです。

ただし、変数に保持されているメソッドを呼び出す場合は、常にクラスインスタンスを提供する必要があります。これは、非表示の「this」パラメーターで渡すことができます。


編集:コメントに応えて

ポインタメンバーの詳細については、別のSOの質問を参照してください。メンバーへのポインタは、クラスインスタンスの先頭と指定されたフィールドの違いを格納していると思います。メンバーへのポインターを使用してフィールドの値を取得しようとすると、コンパイラーはクラス・インスタンスの先頭を見つけ、メンバーへのポインターに格納されているバイト数だけ移動して、指定されたフィールドに到達します。

各クラスインスタンスには、非静的フィールドの独自のコピーがあります。そうでない場合、それらはあまり役に立ちません。

メソッドへのポインターと同様に、メンバーへのポインターを直接使用することはできません。ここでも、クラスインスタンスを提供する必要があります。

私が言うことの証拠は正しいので、ここにあります:

class C
{
public:
    int a;
    int b;
};

// Disassembly of fragment of code:

    int C::*pointerToA = &C::a;
00DB438C  mov         dword ptr [pointerToA],0  
    int C::*pointerToB = &C::b;
00DB4393  mov         dword ptr [pointerToB],4  

pointerToAとpointerToBに格納されている値を確認できますか?フィールドaはクラスインスタンスの先頭から0バイト離れているため、値0はpointerToAに格納されます。一方、フィールドは4バイト長bのフィールドの後に格納aされるため、値4はpointerToBに格納されます。

于 2013-03-03T13:41:53.390 に答える