ダニー・カレブはこれを非常にうまく説明しています:
メンバーへのポインタの基本的な表現
メンバーへのポインターは通常のポインターのように動作しますが、舞台裏ではそれらの表現はまったく異なります。実際、メンバーへのポインターは通常、特定の場合に最大4つのフィールドを含む構造体で構成されます。これは、メンバーへのポインタが、通常のメンバー関数だけでなく、仮想メンバー関数、複数の基本クラスを持つオブジェクトのメンバー関数、および仮想基本クラスのメンバー関数もサポートする必要があるためです。したがって、最も単純なメンバー関数は、2つのポインターのセットとして表すことができます。1つはメンバー関数の物理メモリアドレスを保持し、もう1つはthis
ポインタ。ただし、仮想メンバー関数、多重継承、仮想継承などの場合、メンバーへのポインターは追加情報を格納する必要があります。したがって、メンバーへのポインターを通常のポインターにキャストすることも、異なるタイプのメンバーへのポインター間で安全にキャストすることもできません。
コンパイラがメンバーへのポインタをどのように表すかを理解するには、sizeof
演算子を使用します。次の例では、データメンバーへのポインターとメンバー関数へのポインターのサイズが取得されます。ご覧のとおり、サイズが異なるため、表現も異なります。
struct A
{
int x;
void f();
};
int A::*pmi = &A::x;
void (A::*pmf)() = &A::f;
int n = sizeof (pmi); // 8 byte with my compiler
int m = sizeof (pmf); // 12 bytes with my compiler
問題のクラスおよびメンバー関数が仮想であるかどうかに応じて、これらの各ポインターの表現が異なる場合があることに注意してください。