通过对象指针的方式强行指定到子类_C++中的虚指针与虚函数表
? 最近在逛B站的時候發現有候捷老師的課程,如獲至寶。因此,跟隨他的講解又復習了一遍關于C++的內容,收獲也非常的大,對于某些模糊的概念及遺忘的內容又有了更深的認識。
以下內容是關于虛函數表、虛函數指針,而C++中的動態綁定實現和這兩個內容是分不開的。
虛函數表、虛指針
? 當一個類在實現的時候,如果存在一個或以上的虛函數時,那么這個類便會包含一張虛函數表。而當一個子類繼承并重載了基類的虛函數時,它也會有自己的一張虛函數表。當我們在設計類的時候,如果把某個函數設置成虛函數時,也就表明我們希望子類在繼承的時候能夠有自己的實現方式;如果我們明確這個類不會被繼承,那么就不應該有虛函數的出現。
下面是某個基類A的實現:
class? 從下圖中可以看到該類在內存中的存放形式,對于虛函數的調用是通過查虛函數表來進行的,每個虛函數在虛函數表中都存放著自己的一個地址,而如何在虛函數表中進行查找,則是通過虛指針來調用,在內存結構中它一般都會放在類最開始的地方,而對于普通函數則不需要通過查表操作。這張虛函數表是什么時候被創建的呢?它是在編譯的時候產生,否則這個類的結構信息中也不會插入虛指針的地址信息。
以下例子包含了繼承關系:
class以上三個類在內存中的排布關系如下圖所示:
關于動態綁定
在設計了以上三個類之后,我們就要開始對它們進行使用。
int? 假如在程序中分別創建兩個對象 a 和 b,a的創建是通過將b強制轉化為類A得來的。對于 b.vfunc1() 的調用,應該沒有太的疑問,它所調用的就是類B中的 vfunc1。而對于 a.vfunc1() 的調用,它雖然是強制轉化后的結果,但并不能改變它是一個類A對象的事實,因此這里調用的便是類A中的 vfunc1,也就是上圖中顯示綠色的函數。
int? 將程序改寫成以上內容,pa 是一個類A的指針,但它指向的是一個類B的對象。在使用pa調用 vfunc1 的時候,程序發現pa是一個指針,并且現在正在調用一個虛函數叫做 vfunc1,這時通過 pa->vptr 這個虛指針到類B的虛函數中(上圖的B vtbl)找對應的虛函數地址,找到該地址以后,就用相應的虛函數來進行調用,也就是調用上圖所示的 B::vfunc1()。
pa是類A的指針,為什么查找的是類B的虛函數表?只要某一個類X包含虛函數,無論是它的父類或者它本身擁有,那么這個類的對象都會包含一個虛指針vptr,至于vptr要指向哪張表,取決于類X它本身是否含有虛函數。此處,類B中存在虛函數,那么它就會擁有自己的一張虛函數表。pa指向的是一個類B的對象,因此 p-vptr 指代的是類B中虛指針,所以它查找的是類B的虛函數表
如何從虛函數表中查找到 vfunc1 的地址?
虛函數表中的內容是在編譯的時候確定的,通過以下方式進行查找 (* p->vptr[n] )(p) 或者 (* (p->vptr)[n] )(p),它的解讀是:通過類對象指針p找到虛指針vptr,再查找到虛函數表中的第n個內容,并將他作為函數指針進行調用,調用時的入參是p(式子中的第二個p),而這個p就是隱藏的this指針,這里的n也是在編譯的時候確定的。int
? 再將程序修改成以上內容,對于 p2->vfunc1() 的調用和上文所述一致,它調用的是 B::vfunc1 函數。而對于 p1->vfunc1() 的調用,同樣通過上面的方法可知, p1->vptr 它所指向的是類A的虛函數表,因此它調用的是 A::vfunc1 函數。
? 通過以上內容,我們可以知道在使用基類指針調用虛函數的時候,它能夠根據所指的類對象的不同來正確調用虛函數。而這些能夠正常工作,得益于虛指針和虛函數表的引入,使得在程序運行期間能夠動態調用函數。
動態綁定有以下三項條件要符合:
與動態綁定相對應的是靜態綁定,它屬于編譯的時候就確定下來的,如上文的非虛函數,他們是類對象直接可調用的,而不需要任何查表操作,因此調用的速度也快于虛函數。
參考
總結
以上是生活随笔為你收集整理的通过对象指针的方式强行指定到子类_C++中的虚指针与虚函数表的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DSP C2000汇编语言,DSPC20
- 下一篇: html的课设作业6,第七节课html标