1. 數據成員指針
對於普通指針變量來說,其值是它所指向的地址,0表示空指針。
而對於數據成員指針變量來說,其值是數據成員所在地址相對於對象起始地址的偏移值,空指針用-1表示。例:
代碼示例:

struct X { int a; int b; }; #define VALUE_OF_PTR(p) (*(long*)&p) int main() { int X::*p = 0; // VALUE_OF_PTR(p) == -1 p = &X::a; // VALUE_OF_PTR(p) == 0 p = &X::b; // VALUE_OF_PTR(p) == 4 return 0; }
2. 函數成員指針
函數成員指針與普通函數指針相比,其size為普通函數指針的兩倍(x64下為16字節),分為:ptr和adj兩部分。
(1) 非虛函數成員指針
ptr部分內容為函數指針(指向一個全局函數,該函數的第一個參數為this指針),adj部分始終為0。例:
代碼示例:

extern "C" int printf(const char*, ...); struct B { void foo() { printf("B::foo(): this = 0x%p\n", this); } }; struct D : public B { void bar() { printf("D::bar(): this = 0x%p\n", this); } }; void (B::*pbfoo)() = &B::foo; // ptr: points to _ZN1B3fooEv, adj: 0 void (D::*pdfoo)() = &D::foo; // ptr: points to _ZN1B3fooEv, adj: 0 void (D::*pdbar)() = &D::bar; // ptr: points to _ZN1D3barEv, adj: 0 extern "C" void _ZN1B3fooEv(B*); extern "C" void _ZN1D3barEv(D*); #define PART1_OF_PTR(p) (((long*)&p)[0]) #define PART2_OF_PTR(p) (((long*)&p)[1]) int main() { printf("&B::foo->ptr: 0x%lX\n", PART1_OF_PTR(pbfoo)); printf("&B::foo->adj: 0x%lX\n", PART2_OF_PTR(pbfoo)); // 0 printf("&D::foo->ptr: 0x%lX\n", PART1_OF_PTR(pdfoo)); printf("&D::foo->adj: 0x%lX\n", PART2_OF_PTR(pdfoo)); // 0 printf("&D::bar->ptr: 0x%lX\n", PART1_OF_PTR(pdbar)); printf("&D::bar->adj: 0x%lX\n", PART2_OF_PTR(pdbar)); // 0 D* d = new D(); d->foo(); _ZN1B3fooEv(d); // equal to d->foo() d->bar(); _ZN1D3barEv(d); // equal to d->bar() return 0; }
(2) 虛函數成員指針
ptr部分內容為虛函數對應的函數指針在虛函數表中的偏移地址加1(之所以加1是為了用0表示空指針),而adj部分為調節this指針的偏移字節數。例:
說明:
- A和B都沒有基類,但是都有虛函數,因此各有一個虛函數指針(假設為vptr)。
- C同時繼承了A和B,因此會繼承兩個虛函數指針,但是為了節省空間,C會與主基類A公用一個虛函數指針(即上圖中vptr1),繼承自B的虛函數指針假設為vptr2。
- C沒有重寫繼承自A和B的虛函數,因此在C的虛函數表中存在A::foo和B::bar函數指針(如果C中重寫了foo(),則C的虛函數表中A::foo會被替換為C::foo)。
- C中有兩個虛函數指針vptr1和vptr2,相當於有兩張虛函數表。
- A::foo(C::foo)、B::Bar(C::bar)都在虛函數表中偏移地址為0的位置,因此ptr為1(0+1=1)。而C::quz在偏移為8的位置,因此ptr為9(8+1=9)。
- 當我們使用pc調用C::bar()時,如:“(pc->*pcbar)()”,實際上調用的是B::bar()(即_ZN1B3barEv(pc)),pc需要被轉換為B*類型指針,因此需要對this指針進行調節(調節至pb指向的地址),因此adj為8。
代碼示例:

extern "C" int printf(const char*, ...); struct A { virtual void foo() { printf("A::foo(): this = 0x%p\n", this); } }; struct B { virtual void bar() { printf("B::bar(): this = 0x%p\n", this); } }; struct C : public A, public B { virtual void quz() { printf("C::quz(): this = 0x%p\n", this); } }; void (A::*pafoo)() = &A::foo; // ptr: 1, adj: 0 void (B::*pbbar)() = &B::bar; // ptr: 1, adj: 0 void (C::*pcfoo)() = &C::foo; // ptr: 1, adj: 0 void (C::*pcquz)() = &C::quz; // ptr: 9, adj: 0 void (C::*pcbar)() = &C::bar; // ptr: 1, adj: 8 #define PART1_OF_PTR(p) (((long*)&p)[0]) #define PART2_OF_PTR(p) (((long*)&p)[1]) int main() { printf("&A::foo->ptr: 0x%lX, ", PART1_OF_PTR(pafoo)); // 1 printf("&A::foo->adj: 0x%lX\n", PART2_OF_PTR(pafoo)); // 0 printf("&B::bar->ptr: 0x%lX, ", PART1_OF_PTR(pbbar)); // 1 printf("&B::bar->adj: 0x%lX\n", PART2_OF_PTR(pbbar)); // 0 printf("&C::foo->ptr: 0x%lX, ", PART1_OF_PTR(pcfoo)); // 1 printf("&C::foo->adj: 0x%lX\n", PART2_OF_PTR(pcfoo)); // 0 printf("&C::quz->ptr: 0x%lX, ", PART1_OF_PTR(pcquz)); // 9 printf("&C::quz->adj: 0x%lX\n", PART2_OF_PTR(pcquz)); // 0 printf("&C::bar->ptr: 0x%lX, ", PART1_OF_PTR(pcbar)); // 1 printf("&C::bar->adj: 0x%lX\n", PART2_OF_PTR(pcbar)); // 8 return 0; }
參考:
C++ ABI for Itanium: 2.3 Member Pointers