国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

C++中的多態(tài)與虛函數(shù)的內(nèi)部實現(xiàn)方法

2020-05-23 13:57:38
字體:
供稿:網(wǎng)友

1、什么是多態(tài)

多態(tài)性可以簡單概括為“一個接口,多種行為”。

也就是說,向不同的對象發(fā)送同一個消息, 不同的對象在接收時會產(chǎn)生不同的行為(即方法)。也就是說,每個對象可以用自己的方式去響應(yīng)共同的消息。所謂消息,就是調(diào)用函數(shù),不同的行為就是指不同的實現(xiàn),即執(zhí)行不同的函數(shù)。這是一種泛型技術(shù),即用相同的代碼實現(xiàn)不同的動作。這體現(xiàn)了面向?qū)ο缶幊痰膬?yōu)越性。

多態(tài)分為兩種:

(1)編譯時多態(tài):主要通過函數(shù)的重載和模板來實現(xiàn)。

(2)運行時多態(tài):主要通過虛函數(shù)來實現(xiàn)。

2、幾個相關(guān)概念

(1)覆蓋、重寫(override)

override指基類的某個成員函數(shù)為虛函數(shù),派生類又定義一成員函數(shù),除函數(shù)體的其余部分都與基類的成員函數(shù)相同。注意,如果只是函數(shù)名相同,形參或返回類型不同的話,就不能稱為override,而是hide。

(2)重載(overload)

指同一個作用域出生多個函數(shù)名相同,但是形參不同的函數(shù)。編譯器在編譯的時候,通過實參的個數(shù)和類型,選擇最終調(diào)用的函數(shù)。

(3)隱藏(hide)

分為兩種:

1)局部變量或者函數(shù)隱藏了全局變量或者函數(shù)
2)派生類擁有和基類同名的成員函數(shù)或成員變量。

產(chǎn)生的結(jié)果:使全局或基類的變量、函數(shù)不可見。

3、幾個簡單的例子

/****************************************************************************************************** * File:PolymorphismTest * Introduction:測試多態(tài)的一些特性。 * Author:CoderCong* Date:20141114 * LastModifiedDate:20160113 *******************************************************************************************************/ #include "stdafx.h" #include <iostream> using namespace std; class A { public:   void foo()   {     printf("1/n");   }   virtual void fun()   {     printf("2/n");   } }; class B : public A { public:   void foo() //由于基類的foo函數(shù)并不是虛函數(shù),所以是隱藏,而不是重寫   {     printf("3/n");   }   void fun() //重寫   {     printf("4/n");   } }; int main(void) {   A a;   B b;   A *p = &a;   p->foo(); //輸出1。   p->fun(); //輸出2。   p = &b;   p->foo(); //輸出1。因為p是基類指針,p->foo指向一個具有固定偏移量的函數(shù)。也就是基類函數(shù)   p->fun(); //輸出4。多態(tài)。雖然p是基類指針,但實際上指向的是一個子類對象。p->fun指向的是一個虛函數(shù)。按照動態(tài)類型,調(diào)用子類函數(shù)     return 0; }

4、運行時多態(tài)以及虛函數(shù)的內(nèi)部實現(xiàn)

看了上邊幾個簡單的例子,我恍然大悟,原來這就是多態(tài),這么簡單,明白啦!

好,那我們再看一個例子:

class A { public:   virtual void FunA()   {     cout << "FunA1" << endl;   };   virtual void FunAA()   {     cout << "FunA2" << endl;   } }; class B { public:   virtual void FunB()   {     cout << "FunB" << endl;   } }; class C :public A, public B { public:   virtual void FunA()   {     cout << "FunA1C" << endl;   }; }; int _tmain(int argc, _TCHAR* argv[]) {   C objC;   A *pA = &objC;   B *pB = &objC;   C *pC = &objC;    printf("%d %d/n", &objC, objC);   printf("%d %d/n", pA, *pA);   printf("%d %d/n", pB, *pB);   printf("%d %d/n", pC, *pC);    return 0; }

運行結(jié)果:

5241376 1563032

5241376 1563032

5241380 1563256

5241376 1563032

細(xì)心的同志一定發(fā)現(xiàn)了pB出了問題,為什么明明都是指向objC的指針,pB跟別人的值都不一樣呢?

是不是編譯器出了問題呢?

當(dāng)然不是!我們先講結(jié)論:

(1)每一個含有虛函數(shù)的類,都會生成虛表(virtual table)。這個表,記錄了對象的動態(tài)類型,決定了執(zhí)行此對象的虛成員函數(shù)的時候,真正執(zhí)行的那一個成員函數(shù)。

(2)對于有多個基類的類對象,會有多個虛表,每一個基類對應(yīng)一個虛表,同時,虛表的順序和繼承時的順序相同。

(3)在每一個類對象所占用的內(nèi)存中,虛指針位于最前邊,每個虛指針指向?qū)?yīng)的虛表。

先從簡單的單個基類說起:

class A { public:   virtual void FunA()   {     cout << "FunA1" << endl;   }   virtual void FunA2()   {     cout << "FunA2" << endl;   } };  class C :public A {   virtual void FunA()   {     cout << "FunA1C" << endl;   }}; int _tmain(int argc, _TCHAR* argv[]) {   A *pA = new A;   C *pC = new C;   typedef void (*Fun)(void);    Fun fun= (Fun)*((int*)(*(int*)pA));   fun();//pA指向的第一個函數(shù)   fun = (Fun)*((int*)(*(int*)pA) +1);   fun();//pA指向的第二個函數(shù)      fun = (Fun)*((int*)(*(int*)pC));   fun();//pC指向的第一個函數(shù)   fun = (Fun)*((int*)(*(int*)pC) + 1);   fun();//pC指向的第二個函數(shù)   return 0; }

運行結(jié)果:

FunA1
FunA2
FunA1C
FunA2
是不是有點暈?沒關(guān)系。我一點一點解釋:pA對應(yīng)一個A的對象,我們可以畫出這樣的一個表:
      C++,多態(tài)與虛函數(shù)
這就是對象*pA的虛表,兩個虛函數(shù)以聲明順序排列。pA指向?qū)ο?pA,則*(int*)pA指向此虛擬表,則(Fun)*((int*)(*(int*)pA))指向FunA,同理,(Fun)*((int*)(*(int*)pA) + 1)指向FunA2。所以,出現(xiàn)了前兩個結(jié)果。
根據(jù)后兩個結(jié)果, 我們可以推測*pC的虛表如下圖所示:
      C++,多態(tài)與虛函數(shù)
也就是說,由于C中的FunA重寫(override)了A中的FunA,虛擬表中虛擬函數(shù)的地址也被重寫了。
就是這樣,這就是多態(tài)實現(xiàn)的內(nèi)部機(jī)制。
我們再回到最初的問題:為什么*pB出了問題。
根據(jù)上邊的結(jié)論,我們大膽地進(jìn)行猜測:由于C是由A、B派生而來,所以objC有兩個虛擬表,而由于表的順序,pA、pC都指向了對應(yīng)于A的虛擬表,而pB則指向了對應(yīng)于B的虛擬表。做個實驗來驗證我們的猜想是否正確:
我們不改變A、B、C類,將問題中的main改一下:
int _tmain(int argc, _TCHAR* argv[]) {   C objC;   A *pA = &objA;   B *pB = &objC;   C *pC = &objC;      typedef void (*Fun)(void);    Fun fun = (Fun)*((int*)(*(int*)pC));   fun();//第一個表第一個函數(shù)   fun = (Fun)*((int*)(*(int*)pC)+1);   fun();//第一個表第二個函數(shù)   fun = (Fun)*((int*)(*((int*)pC+1)));   fun();<span style="white-space:pre"> </span>//第二個表第一個函數(shù)   fun = (Fun)*((int*)(*(int*)pB));   fun();//pB指向的表的第一個函數(shù)   return 0; }

哈哈,和我們的猜測完全一致:

FunA1C
FunA2
FunB
FunB
我們可以畫出這樣的虛函數(shù)圖:
        C++,多態(tài)與虛函數(shù)
暫且這樣理解,編譯器執(zhí)行B *pB = &objC時不是僅僅是賦值,而是做了相應(yīng)的優(yōu)化,將pB指向了第二張?zhí)摫怼?/span>
說了這么多,我是只是簡單地解釋了虛函數(shù)的實現(xiàn)原理,可究竟對象的內(nèi)部的內(nèi)存布局是怎樣的?類數(shù)據(jù)成員與多個虛表的具體內(nèi)存布局又是怎樣的?編譯器是如何在賦值的時候作了優(yōu)化的呢?我在以后的時間里會講一下。

以上就是小編為大家?guī)淼腃++中的多態(tài)與虛函數(shù)的內(nèi)部實現(xiàn)方法全部內(nèi)容了,希望大家多多支持VEVB武林網(wǎng)~


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 德安县| 聂荣县| 日照市| 时尚| 铜梁县| 丽水市| 马公市| 集安市| 平度市| 孟州市| 铁岭县| 卫辉市| 钦州市| 修水县| 赤峰市| 辰溪县| 清原| 和林格尔县| 桐庐县| 仪陇县| 泰和县| 乌鲁木齐县| 白水县| 留坝县| 辉县市| 衡山县| 扶风县| 麦盖提县| 大兴区| 新巴尔虎右旗| 蓬溪县| 临夏县| 道孚县| 定南县| 阿勒泰市| 高唐县| 汪清县| 威海市| 三都| 长治市| 视频|