繼續第四章的内容整理,這一部分也是第四章的最後一部分,是關于成員函數指針的,第三章下的内容整理是關于成員變量指針的,這個則是成員函數指針,二者可以互為參考。
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函數的内容整理。