日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++是如何实现多态的

發(fā)布時間:2023/12/2 c/c++ 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++是如何实现多态的 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

C++是如何實現(xiàn)多態(tài)的

結論:C++通過虛函數(shù)來實現(xiàn)多態(tài)的,根本原因是派生類和基類的虛函數(shù)表的不同

構成多態(tài)的必要條件有如下3點:

  • 存在繼承關系
  • 基類存在虛函數(shù),且派生類有相同原型的函數(shù)遮蔽它
  • 存在基類類型的指針指向派生類對象,且該指針調用了存在遮蔽關系的虛函數(shù)

如下圖,就是一個簡單的多態(tài)的例子:(實驗環(huán)境:vs2019)

大豬眉頭一皺,覺得事情并不簡單。

針對上述實驗結果,有個疑點:

  • 基類指針是如何知道要調用派生類的f函數(shù),而不是基類的f函數(shù)的呢?

這其中的奧秘,還是要從內(nèi)存模型開始探索。談到內(nèi)存模型,那么我們先探討最簡單的——這個對象有多大吧(或者說這個對象占幾個字節(jié)的內(nèi)存吧)。

如上圖所示,A的大小在32位編譯模式下是4字節(jié),在64位編譯模式下是8字節(jié),由此可見,A內(nèi)存模型里面肯定有個指針!(原因:https://blog.csdn.net/qq_18138105/article/details/105209406)

那么繼續(xù)深入分析對象的內(nèi)存模型,這個指針到底指向什么呢?

實際上,這個指針指向一個數(shù)組,數(shù)組中的每個元素都是虛函數(shù)的入口地址,這個數(shù)組也就是虛函數(shù)表(存在于C++內(nèi)存模型中的常量區(qū))!由于虛函數(shù)表和對象在內(nèi)存上是分開存儲的(虛函數(shù)在C++內(nèi)存模型中的代碼區(qū),對象在C++內(nèi)存模型中的堆區(qū),指向對象的指針在C++內(nèi)存模型的棧區(qū)),因此,就需要在對象中需要安插一個指向這個虛函數(shù)表的指針!

我們再看一個例子:

各個對象的內(nèi)存模型如下(我用cocos creator畫的):

如上圖所示,對象內(nèi)存和虛函數(shù)表內(nèi)存分開的,對象的*vfptr指向對應的虛函數(shù)表。(注意:和很多博客畫的圖不同,為了直觀!我是把處于內(nèi)存高地址的放上面,處于內(nèi)存低地址的放下面)

仔細觀察派生類B的虛函數(shù)表

  • 派生類如果存在對基類有遮蔽關系的虛函數(shù),則在虛函數(shù)表中則取派生類的這個虛函數(shù)的入口地址,如&B::f
  • 對于未被派生類遮蔽的基類的虛函數(shù),派生類的虛函數(shù)表則取基類的這個虛函數(shù)的入口地址,如&A::g
  • 派生類新增的虛函數(shù),依次往虛函數(shù)表后面加,如&B::h

因此,我們通過指針調用虛函數(shù)時,先根據(jù)指針找到對象里的vfptr來定位到虛函數(shù)表,然后通過虛函數(shù)在虛函數(shù)表中的索引值來得到虛函數(shù)的入口地址。

比如 a->f(), 實際上編譯器會這么處理 (*(*(a+0)+0))(a)

  • a+0 是 a對象 vfptr 的地址
  • *(a+0)是 vfptr的值, 又 vfptr 是指向虛函數(shù)表的指針,因此 *(a+0) 也是虛函數(shù)表的首地址
  • 由于 A::f 函數(shù)在虛函數(shù)表中的索引是0,因此 (*(a+0)+0)就是獲取 A::f 函數(shù)的入口地址
  • 知道了A::f 函數(shù)的地址,*(*(a+0)+0) 就是對 A::f 的調用
  • 把a對象的指針傳入 A::f, 就是a作為 A::f 的 this指針!

同理,調用 b->f() ,也是一樣的,只不過訪問的是 B的虛函數(shù)表,最后調用的是 B::f, 而不是 A::f, 這就解釋了 “基類指針是如何知道要調用派生類的f函數(shù),而不是基類的f函數(shù)” 的問題,就是因為虛函數(shù)表的不同

疑問雖然已經(jīng)解決了,但是我們還是要繼續(xù)細探究竟!經(jīng)過下面的實驗,得出的 結果和內(nèi)存模型完全相符!

#include <iostream> using namespace std;class A { public:A() : a(100) {}virtual void f() {cout << "A::f" << endl;}virtual void g() {cout << "A::g" << endl;} protected:int a; };class B : public A { public:B() : b(50) {}virtual void f() {cout << "B::f" << endl;}virtual void h() {cout << "B::h" << endl;} protected:int b; };// 定義一個 參數(shù)為 A*類型 返回值是 void 的 函數(shù)指針 Fun typedef void (*Fun)(A*);// 指針值類型(64位編譯模式下是long long, 32位編譯模式下是int) #ifdef _WIN64 #define ptr_value long long #else #define ptr_value int #endif // 根據(jù)對象指針和偏移量 獲取 指針值類型的指針 #define ptr(obj, offset) ((ptr_value*)obj+offset)int main() {A* a = new A;A* b = new B;// 接下來探究 a對象 和 b對象 的 內(nèi)存模型 //ptr_value a_vfptr_value = *ptr(a, 0); // a_vfptr的值 即 b的虛函數(shù)表的首地址((Fun)*ptr(a_vfptr_value, 0))(a); // A::f((Fun)*ptr(a_vfptr_value, 1))(a); // A::g ptr_value a_a = *ptr(a, 1); // a對象 的 int a成員cout << (int)a_a << endl; // 100ptr_value b_vfptr_value = *ptr(b, 0); // b_vfptr的值 即 b的虛函數(shù)表的首地址((Fun)*ptr(b_vfptr_value, 0))(b); // B::f((Fun)*ptr(b_vfptr_value, 1))(b); // A::g ((Fun)*ptr(b_vfptr_value, 2))(b); // B::hptr_value b_a = *ptr(b, 1); // b對象 的 int a成員cout << (int)b_a << endl; // 100ptr_value b_b = *ptr(b, 2); // b對象 的 int b成員cout << (int)b_b << endl; // 50return 0; }

因此,只要理解了虛函數(shù)表,C++的多態(tài)自然就迎刃而解了。

總結

以上是生活随笔為你收集整理的C++是如何实现多态的的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。