天天看點

[讀書筆記] 深入探索C++對象模型-第四章-Function語義學(下)

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

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函數的内容整理。

繼續閱讀