關(guān)于虛函數(shù)實現(xiàn)多態(tài)的原理及分析
1、C++中如何實現(xiàn)多態(tài)
- 基類中先聲明一個虛函數(shù)
- 至少有一個繼承該基類的子類
2、虛函數(shù)實現(xiàn)多態(tài)的原理
- 當(dāng)一個類中出現(xiàn)虛函數(shù)或著子類繼承了虛函數(shù)時,就會在該類中產(chǎn)生一個虛函數(shù)表(virtual table),虛函數(shù)表實際上是一個函數(shù)指針數(shù)組(在有的編譯器作用下是鏈表),里面的每一個元素對應(yīng)指向該類中的某一個虛函數(shù)的指針。
- 被該類聲明的對象會包含一個虛函數(shù)表指針(virtual table pointer),指向該類的虛函數(shù)表的地址。
- 虛函數(shù)的調(diào)用過程: 當(dāng)一個對象要調(diào)用到虛函數(shù)時,先將對象內(nèi)存中的vptr指針(虛函數(shù)表指針)指向定義該類的vtbl(虛函數(shù)表),vtbl再尋找里面的指針指向想要調(diào)用的虛函數(shù),從而完成虛函數(shù)的調(diào)用。
2.1 單類繼承
定義一個父類
class Person{ public: virtual void f(){cout << "use f()" << endl;} virtual void g(){cout << "use g()" << endl;} virtual void h(){cout << "use h()" << endl;} };
父類對象其在內(nèi)存中布局如下:
- 再定義一個子類,此時并不覆蓋父類的虛函數(shù):
class Bag:public Person{ public: virtual void i(){cout << "use i()" << endl;} virtual void j(){cout << "use j()" << endl;} };
可以看出虛函數(shù)表內(nèi)的虛函數(shù)是按聲明順序進行排序的
父類虛函數(shù)排在子類虛函數(shù)之前
- 當(dāng)我們把子類中的虛函數(shù)覆蓋掉:(修改Bag類)
class Bag:public Person{ public: void f(){cout << "class Bag use fun" << endl;} virtual void i(){cout << "use i()" << endl;} virtual void j(){cout << "use j()" << endl;} };
子類覆蓋的虛函數(shù),放在父類原先放該虛函數(shù)的位置上。
所以當(dāng)父類指針指向該子類對象時,會調(diào)用該子類的重載函數(shù)
2.2 多類繼承
- 子類沒有覆蓋父類的虛函數(shù)
子類的虛函數(shù)放在第一張?zhí)摵瘮?shù)表中,緊跟著第一個父類的虛函數(shù)
如果每個父類都有虛函數(shù),則有幾個父類就有幾張?zhí)摵瘮?shù)表
- 子類覆蓋父類的虛函數(shù)
父類的虛函數(shù)被子類覆蓋后,則該子類對應(yīng)的重載函數(shù)的位置在被覆蓋的父類函數(shù)的位置上。(如果父類沒有該虛函數(shù),則不用被覆蓋)
父類的虛函數(shù)被子類覆蓋后,則父類指針指向該子類對象時,調(diào)用的f()便是子類中重載的f()
示例
#include <iostream> using namespace std; class Person1{ public: virtual void f(){} virtual void g(){} virtual void h(){} virtual ~Person1(){} }; class Person2{ public: virtual void f(){} virtual void g(){} virtual void h(){} void a(){ // 成員函數(shù),不需要重載 cout << "class Person2" << endl; } virtual ~Person2(){} }; class Person3{ public: virtual void g(){} virtual void h(){} virtual ~Person3(){} }; class Bag:public Person1, public Person2, public Person3{ public: void f(){ cout << "Bag f()" << endl; } void g(){ cout << "Bag g()" << endl; } void a(){ cout << "Class Bag" << endl; } }; int main(int argc, char const *argv[]) { Person3* p3 = new Bag; //p3->f(); // P3 沒有成員函數(shù)f() // 多態(tài)首先得是 父類有虛函數(shù),其次是子類要定義該函數(shù)的重載 // 如果父類的虛函數(shù)改為成員函數(shù),則子類無法進行重載,即無法實現(xiàn)多態(tài) delete p3; p3 = NULL; Person1* p1 = new Bag; p1->f(); delete p1; p1 = NULL; Person2* p2 = new Bag; p2->a(); delete p2; p2 = NULL; return 0; }
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
C語言驅(qū)動開發(fā)之通過ReadFile與內(nèi)核層通信
驅(qū)動與應(yīng)用程序的通信是非常有必要的,內(nèi)核中執(zhí)行代碼后需要將其動態(tài)顯示給應(yīng)用層。為了實現(xiàn)內(nèi)核與應(yīng)用層數(shù)據(jù)交互則必須有通信的方法,微軟為我們提供了三種通信方式,本文先來介紹通過ReadFile系列函數(shù)實現(xiàn)的通信模式2022-09-09VisualStudio2022制作多項目模板及Vsix插件的實現(xiàn)
本文主要介紹了VisualStudio2022制作多項目模板及Vsix插件的實現(xiàn),文中通過圖文介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-06-06C++歸并法+快速排序?qū)崿F(xiàn)鏈表排序的方法
這篇文章主要介紹了C++歸并法+快速排序?qū)崿F(xiàn)鏈表排序的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04C語言中((type *)0) 和(type *0)區(qū)別小結(jié)
((type *)0)?和?(type *0)?在 C 和 C++ 中有不同的含義和用途,本文主要介紹了C語言中((type *)0) 和(type *0)區(qū)別,具有一定的參考價值,感興趣的可以了解一下2024-08-08