關(guān)于虛函數(shù)實(shí)現(xiàn)多態(tài)的原理及分析
1、C++中如何實(shí)現(xiàn)多態(tài)
- 基類中先聲明一個(gè)虛函數(shù)
- 至少有一個(gè)繼承該基類的子類
2、虛函數(shù)實(shí)現(xiàn)多態(tài)的原理
- 當(dāng)一個(gè)類中出現(xiàn)虛函數(shù)或著子類繼承了虛函數(shù)時(shí),就會(huì)在該類中產(chǎn)生一個(gè)虛函數(shù)表(virtual table),虛函數(shù)表實(shí)際上是一個(gè)函數(shù)指針數(shù)組(在有的編譯器作用下是鏈表),里面的每一個(gè)元素對(duì)應(yīng)指向該類中的某一個(gè)虛函數(shù)的指針。
- 被該類聲明的對(duì)象會(huì)包含一個(gè)虛函數(shù)表指針(virtual table pointer),指向該類的虛函數(shù)表的地址。
- 虛函數(shù)的調(diào)用過程: 當(dāng)一個(gè)對(duì)象要調(diào)用到虛函數(shù)時(shí),先將對(duì)象內(nèi)存中的vptr指針(虛函數(shù)表指針)指向定義該類的vtbl(虛函數(shù)表),vtbl再尋找里面的指針指向想要調(diào)用的虛函數(shù),從而完成虛函數(shù)的調(diào)用。

2.1 單類繼承
定義一個(gè)父類
class Person{
public:
virtual void f(){cout << "use f()" << endl;}
virtual void g(){cout << "use g()" << endl;}
virtual void h(){cout << "use h()" << endl;}
};
父類對(duì)象其在內(nèi)存中布局如下:

- 再定義一個(gè)子類,此時(shí)并不覆蓋父類的虛函數(shù):
class Bag:public Person{
public:
virtual void i(){cout << "use i()" << endl;}
virtual void j(){cout << "use j()" << endl;}
};

可以看出虛函數(shù)表內(nèi)的虛函數(shù)是按聲明順序進(jìn)行排序的
父類虛函數(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)父類指針指向該子類對(duì)象時(shí),會(huì)調(diào)用該子類的重載函數(shù)
2.2 多類繼承
- 子類沒有覆蓋父類的虛函數(shù)

子類的虛函數(shù)放在第一張?zhí)摵瘮?shù)表中,緊跟著第一個(gè)父類的虛函數(shù)
如果每個(gè)父類都有虛函數(shù),則有幾個(gè)父類就有幾張?zhí)摵瘮?shù)表
- 子類覆蓋父類的虛函數(shù)

父類的虛函數(shù)被子類覆蓋后,則該子類對(duì)應(yīng)的重載函數(shù)的位置在被覆蓋的父類函數(shù)的位置上。(如果父類沒有該虛函數(shù),則不用被覆蓋)
父類的虛函數(shù)被子類覆蓋后,則父類指針指向該子類對(duì)象時(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ù),則子類無法進(jìn)行重載,即無法實(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é)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
VS2022新建項(xiàng)目時(shí)沒有ASP.NET Web應(yīng)用程序(.NET Framework)
本文主要介紹了VS2022新建項(xiàng)目時(shí)沒有ASP.NET Web應(yīng)用程序的解決,文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-10-10
在vs2010中,輸出當(dāng)前文件路徑與源文件當(dāng)前行號(hào)的解決方法
本篇文章是對(duì)在vs2010中,輸出當(dāng)前文件路徑與源文件當(dāng)前行號(hào)的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
OpenMP task construct 實(shí)現(xiàn)原理及源碼示例解析
這篇文章主要為大家介紹了OpenMP task construct 實(shí)現(xiàn)原理及源碼示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
VC判斷進(jìn)程是否具有administrator權(quán)限的方法
這篇文章主要介紹了VC判斷進(jìn)程是否具有administrator權(quán)限的方法,在Windows應(yīng)用程序設(shè)計(jì)中具有一定的實(shí)用價(jià)值,需要的朋友可以參考下2014-10-10
C++ GDI實(shí)現(xiàn)圖片格式轉(zhuǎn)換
GDI+(Graphics Device Interface Plus)是一種用于圖形繪制和圖像處理的應(yīng)用程序編程接口(API),在Windows平臺(tái)上廣泛使用,本文就來介紹一下如何使用GDI實(shí)現(xiàn)圖片格式轉(zhuǎn)換吧2023-12-12

