關於C 成員函式指標的一些探索

2021-06-23 08:07:20 字數 1638 閱讀 5238

這幾天看《深入探索c++物件模型》,遇到乙個問題,思考了好久,是關於成員函式指標的。看下面這段**

class base

virtual void y()

};class derived : public base

virtual void y() override

};int main()

其在visual studio2013上的執行結果是:

對於前兩個輸出,沒有問題,因為ptr1指向的是base::x,但是編譯器是如何實現的呢?

首先,這兩個類base和derived都包含虛函式,即在物件的記憶體布局的第乙個位置都有乙個指向虛函式表的vptr,這樣的話,派生類derived的物件derived呼叫其基類base的函式時就不需要對this指標進行調整。所以編譯內部返回的成員函式指標實際上是這樣的:

struct pmf_type1;

其中vcall_addr指向的是編譯器對類的成員函式進行name mangled之後的函式的實際位址。所以用(derived.*ptr1)和(pderived->*ptr1)實際上呼叫的derived的base subobject的x()函式。

其次,令我一直糾結的問題是後面兩個輸出,按照我原來的理解,其輸出應該也是base y,但是實際上完全不是。我表示完全不能接受啊,有木有,後來經過我搜尋相關資料,終於知道為什麼了。因為base::y是virtual function,vc++採用了一種名為thunk的技術來實現virtual function的成員函式指標。同上,由於不需要進行this指標調整,所以ptr2指標在記憶體中的實際型別跟ptr1的完全一樣,但是其中vcall_addr的意義就完全不同了。此vcall_addr指向的並不是虛函式的實際位址,也沒法指向其實際位址。那vcall_addr指向的是**呢?我們先來看下面一段**:

template void vcall(void* this)

你可能會覺得奇怪,這段**是**來的,為什麼會有這樣一段**。我們知道虛函式的呼叫時執行時才知道的,為了支援這個功能,大部分編譯器都採用的vptr->vtbl的模式,將虛函式儲存在visual table內,並在物件內部(一般是第乙個位置)建立乙個指向該visual table的指標。上面那段**,是編譯為每個visual function依據visual function在visual table中的位置生成的**,而我們剛剛說的visual function的成員函式指標中的vcall_addr指向的就是這段**。所以(pderived->*ptr2)呼叫時,實際上呼叫的其vptr在該位置上的函式,也就是derived::y()。

但是我還有個疑問,現在還沒弄懂,就是為什麼(derived.*ptr2)()和(pderived->*ptr2)()的結果一樣。按照我之前的理解,使用.運算子呼叫成員函式不應該有多型效果啊。所以我猜測,由於虛函式的成員函式指標的特殊性,(derived.*ptr2)()和(pderived->*ptr2)()在編譯器看來應該是一樣的。

(derived.*ptr2)()被轉換成 vcall(&derived);

(pderived->*ptr2)()被轉換成vcall(pderived);

所以產生的結果是一樣的,這是猜測,求證實啊。

成員函式指標的一些奇怪行為

因為它們都使用了同乙個this指標,乙個指向基類成員函式的指標可以被當做是指向derived2的成員函式指標來使用,不需要進行任何的轉換操作。在單繼承中,指向乙個類的成員函式指標的大小就是該平台指標的大小。但是如果你使用到了多重繼承,則事情就開始變得有趣起來 這個時候,就會存在兩個可能的this指標...

關於C指標的一些理解

有時總被指標的用法及型別所困擾,那我簡單理解一下指標。指標是乙個特殊的變數,它裡面儲存的數值被解釋成為記憶體裡的乙個位址。要搞清乙個指標需要搞清指標的四方面的內容 指標的型別,指標所指向的型別,指標的值或者叫指標所指向的記憶體區,還有指標本身所佔據的記憶體區。如下例子 1 int ptr 2 cha...

關於類的c成員和函式一些知識 1

1.若是類為空,則它的大小為1 class a sizeof a 1 c 規定兩個物件不能有相同的起始位址空間.2.用struct和class定義的類的預設訪問許可權以及繼承許可權是不一樣的.struct定義的類的預設訪問許可權和繼承許可權是公有的 public class定義的類的預設訪問許可權和...