繼續第四章的內容整理,這一部分也是第四章的最後一部分,是關於成員函數指標的,第三章下的內容整理是關於成員變數指標的,這個則是成員函數指標,二者可以互為參考。
1. 非虛成員函數指標(非靜態)。
取一個非靜態成員函數的地址,如果該函數是非虛函數,則得到的是它在記憶體中的真正地址,但是該地址並不完全,需要綁定與具體的類執行個體(對象)之上,藉助對象的地址(this指標)才可以被調用,例如:一個成員函數指標
double (Point::* pmf)();
經過初始化:
double (Point::*coord)() = &Point::getX;//或者這樣初始化它:coord = &Point::getX;
這樣來調用它:
(orgin.*coord)();//或者這樣(ptr->*coord)();
調用會轉化成:
(coord)(&origin);//或者(coord)(ptr);
靜態成員函數由於沒有this指標,所以指向它的指標是函數指標,而不是指向成員函數的指標。
2. 指向虛成員函數的指標。
當一個函數指標指向一個虛成員函數時,例如:
float (Point::*pmvf)() = &Point::z;//z為虛函數Point *ptr = new Point3d;//Point3d為Point的子類
那麼,當ptr通過該函數指標調用z時,多態機制仍會生效,也就是如下調用,調用的仍是Point3d的z()函數。
(ptr->*pmvf)();//效果等同於ptr->z();
這是因為,取去函數的地址,,得到的是其在虛表中的索引值,也就是對於如下類:
class Point {public: virtual ~ Point(); float x(); float y(); virtual float z();};
&Point::~Point得到的結果是1, &Point::x和&Point::y得到的是它們在記憶體中的地址,因為它們並非虛函數,而&Point::z結果為2,因為它位於虛表的第三個位置(索引從0開始),所以通過上面例子中的pmvf調用函數,會轉化為:
(*ptr->vptr[(int)pvmf])(ptr);//調用Point3d::z()
3. 多重繼承下,指向成員函數的指標。
由於多重繼承(包括多重虛擬繼承)涉及到,子類中可能存在多個虛表,this指標的可能需要調整位移,書中舉例了cfront的實現方法,引入一個結構體,在需要的時候儲存上述內容,結構體如下:
struct __mptr { int delta;//虛基類或者多重繼承下的第二個以及之後基類的this指標位移 int index;//虛函數索引,非虛函數此值為-1 union { ptrtofunc faddr;//非虛函數地址 int v_offset;//虛基類或者多重繼承下的第二個以及之後基類的虛表位置 };};
在此模型下,以下調用:
(ptr->*pmvf)();
會變成:
(pmvf.index < 0) ?(*pmvf.faddr)(ptr)//非虛函數: (*ptr->vptr[pmvf.index])(ptr);//虛函數
關於成員函數指標的內容就整理這些,之後還有一點關於inline函數的內容整理。