最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解C++虛函數(shù)的工作原理

 更新時(shí)間:2020年06月22日 11:34:41   作者:讓我思考一下  
這篇文章主要介紹了C++虛函數(shù)的工作原理的的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下

靜態(tài)綁定與動(dòng)態(tài)綁定

討論靜態(tài)綁定與動(dòng)態(tài)綁定,首先需要理解的是綁定,何為綁定?函數(shù)調(diào)用與函數(shù)本身的關(guān)聯(lián),以及成員訪問與變量內(nèi)存地址間的關(guān)系,稱為綁定。 理解了綁定后再理解靜態(tài)與動(dòng)態(tài)。

  • 靜態(tài)綁定:指在程序編譯過程中,把函數(shù)調(diào)用與響應(yīng)調(diào)用所需的代碼結(jié)合的過程,稱為靜態(tài)綁定。發(fā)生在編譯期。
  • 動(dòng)態(tài)綁定:指在執(zhí)行期間判斷所引用對象的實(shí)際類型,根據(jù)實(shí)際的類型調(diào)用其相應(yīng)的方法。程序運(yùn)行過程中,把函數(shù)調(diào)用與響應(yīng)調(diào)用所需的代碼相結(jié)合的過程稱為動(dòng)態(tài)綁定。發(fā)生于運(yùn)行期。

C++中動(dòng)態(tài)綁定

在C++中動(dòng)態(tài)綁定是通過虛函數(shù)實(shí)現(xiàn)的,是多態(tài)實(shí)現(xiàn)的具體形式。而虛函數(shù)是通過虛函數(shù)表實(shí)現(xiàn)的。這個(gè)表中記錄了虛函數(shù)的地址,解決繼承、覆蓋的問題,保證動(dòng)態(tài)綁定時(shí)能夠根據(jù)對象的實(shí)際類型調(diào)用正確的函數(shù)。這個(gè)虛函數(shù)表在什么地方呢?C++標(biāo)準(zhǔn)規(guī)格說明書中說到,編譯器必須要保證虛函數(shù)表的指針存在于對象實(shí)例中最前面的位置(這是為了保證正確取到虛函數(shù)的偏移量)。也就是說,我們可以通過對象實(shí)例的地址得到這張?zhí)摵瘮?shù)表,然后可以遍歷其中的函數(shù)指針,并調(diào)用相應(yīng)的函數(shù)。

虛函數(shù)的工作原理

要想弄明白動(dòng)態(tài)綁定,就必須弄懂虛函數(shù)的工作原理。C++中虛函數(shù)的實(shí)現(xiàn)一般是通過虛函數(shù)表實(shí)現(xiàn)的(C++規(guī)范中沒有規(guī)定具體用哪種方法,但大部分的編譯器廠商都選擇此方法)。類的虛函數(shù)表是一塊連續(xù)的內(nèi)存,每個(gè)內(nèi)存單元中記錄一個(gè)JMP指令的地址。編譯器會(huì)為每個(gè)有虛函數(shù)的類創(chuàng)建一個(gè)虛函數(shù)表,該虛函數(shù)表將被該類的所有對象共享。 類的每個(gè)虛成員占據(jù)虛函數(shù)表中的一行。如果類中有N個(gè)虛函數(shù),那么其虛函數(shù)表將有N*4字節(jié)的大小。

虛函數(shù)(virtual)是通過虛函數(shù)表來實(shí)現(xiàn)的,在這個(gè)表中,主要是一個(gè)類的虛函數(shù)的地址表,這張表解決了繼承、覆蓋的問題,保證其真實(shí)反映實(shí)際的函數(shù)。這樣,在有虛函數(shù)的類的實(shí)例中分配了指向這個(gè)表的指針的內(nèi)存(位于對象實(shí)例的最前面),所以,當(dāng)用父類的指針來操作一個(gè)子類的時(shí)候,這張?zhí)摵瘮?shù)表就顯得尤為重要,指明了實(shí)際所應(yīng)調(diào)用的函數(shù)。它是如何指明的呢?后面會(huì)講到。

JMP指令是匯編語言中的無條件跳轉(zhuǎn)指令,無條件跳轉(zhuǎn)指令可轉(zhuǎn)到內(nèi)存中任何程序段。轉(zhuǎn)移地址可在指令中給出,也可以在寄存器中給出,或在儲(chǔ)存器中指出。

首先我們定義一個(gè)帶有虛函數(shù)的基類

class Base
{
public:
	virtual void fun1(){
		cout<<"base fun1!\n";
	}
	virtual void fun2(){
		cout<<"base fun2!\n";
	}
	virtual void fun3(){
		cout<<"base fun3!\n";
	}

	int a;
};

我們可以看到在Base類的內(nèi)存布局上,第一個(gè)位置上存放虛函數(shù)表指針,接下來才是Base的成員變量。另外,存在著虛函數(shù)表,該表里存放著Base類的所有virtual函數(shù)。

既然虛函數(shù)表指針通常放在對象實(shí)例的最前面的位置,那么我們應(yīng)該可以通過代碼來訪問虛函數(shù)表,通過下面這段代碼加深對虛函數(shù)表的理解:

#include "stdafx.h"
#include<iostream>
using namespace std;

class Base
{
public:
	virtual void fun1(){
		cout<<"base fun1!\n";
	}
	virtual void fun2(){
		cout<<"base fun2!\n";
	}
	virtual void fun3(){
		cout<<"base fun3!\n";
	}

	int a;
};

int _tmain(int argc, _TCHAR* argv[])
{
	typedef void(*pFunc)(void);
	Base b;
	cout<<"虛函數(shù)表指針地址:"<<(int*)(&b)<<endl;

	//對象最前面是指向虛函數(shù)表的指針,虛函數(shù)表中存放的是虛函數(shù)的地址
	pFunc pfun;
	pfun=(pFunc)*((int*)(*(int*)(&b))); //這里存放的都是地址,所以才一層又一層的指針
	pfun();
	pfun=(pFunc)*((int*)(*(int*)(&b))+1);
	pfun();
	pfun=(pFunc)*((int*)(*(int*)(&b))+2);
	pfun();

	system("pause");
	return 0;
}

運(yùn)行結(jié)果:

通過這個(gè)例子,對虛函數(shù)表指針,虛函數(shù)表這些有了足夠的理解。下面再深入一些。C++又是如何利用基類指針和虛函數(shù)來實(shí)現(xiàn)多態(tài)的呢?這里,我們就需要弄明白在繼承環(huán)境下虛函數(shù)表是如何工作的。目前只理解單繼承,至于虛繼承,多重繼承待以后再理解。

單繼承代碼如下:

class Base
{
public:
	virtual void fun1(){
		cout<<"base fun1!\n";
	}
	virtual void fun2(){
		cout<<"base fun2!\n";
	}
	virtual void fun3(){
		cout<<"base fun3!\n";
	}

	int a;
};

class Child:public Base
{
public:
	void fun1(){
		cout<<"Child fun1\n";
	}
	void fun2(){
		cout<<"Child fun2\n";
	}
	virtual void fun4(){
		cout<<"Child fun4\n";
	}
};

內(nèi)存布局對比:

通過對比,我們可以看到:

  • 在單繼承中,Child類覆蓋了Base類中的同名虛函數(shù),在虛函數(shù)表中體現(xiàn)為對應(yīng)位置被Child類中的新函數(shù)替換,而沒有被覆蓋的函數(shù)則沒有發(fā)生變化。
  • 對于子類自己的虛函數(shù),直接添加到虛函數(shù)表后面。

另外,我們注意到,類Child和類Base中都只有一個(gè)vfptr指針,前面我們說過,該指針指向虛函數(shù)表,我們分別輸出類Child和類Base的vfptr:

int _tmain(int argc, _TCHAR* argv[])
{
	typedef void(*pFunc)(void);
	Base b;
	Child c;
	cout<<"Base類的虛函數(shù)表指針地址:"<<(int*)(&b)<<endl;
	cout<<"Child類的虛函數(shù)表指針地址:"<<(int*)(&c)<<endl;

	system("pause");
	return 0;
}

運(yùn)行結(jié)果:

可以看到,類Child和類Base分別擁有自己的虛函數(shù)表指針vfptr和虛函數(shù)表vftable。

下面這段代碼,說明了父類和基類擁有不同的虛函數(shù)表,同一個(gè)類擁有相同的虛函數(shù)表,同一個(gè)類的不同對象的地址(存放虛函數(shù)表指針的地址)不同。

int _tmain(int argc, _TCHAR* argv[])
{
	Base b;
	Child c1,c2;
	cout<<"Base類的虛函數(shù)表的地址:"<<(int*)(*(int*)(&b))<<endl;
	cout<<"Child類c1的虛函數(shù)表的地址:"<<(int*)(*(int*)(&c1))<<endl;	//虛函數(shù)表指針指向的地址值
	cout<<"Child類c2的虛函數(shù)表的地址:"<<(int*)(*(int*)(&c2))<<endl;

	system("pause");
	return 0;
}

在定義該派生類對象時(shí),先調(diào)用其基類的構(gòu)造函數(shù),然后再初始化vfptr,最后再調(diào)用派生類的構(gòu)造函數(shù)( 從二進(jìn)制的視野來看,所謂基類子類是一個(gè)大結(jié)構(gòu)體,其中this指針開頭的四個(gè)字節(jié)存放虛函數(shù)表頭指針。執(zhí)行子類的構(gòu)造函數(shù)的時(shí)候,首先調(diào)用基類構(gòu)造函數(shù),this指針作為參數(shù),在基類構(gòu)造函數(shù)中填入基類的vfptr,然后回到子類的構(gòu)造函數(shù),填入子類的vfptr,覆蓋基類填入的vfptr。如此以來完成vfptr的初始化)。也就是說,vfptr指向vftable發(fā)生在構(gòu)造函數(shù)期間完成的。

動(dòng)態(tài)綁定例子:

#include "stdafx.h"
#include<iostream>
using namespace std;

class Base
{
public:
	virtual void fun1(){
		cout<<"base fun1!\n";
	}
	virtual void fun2(){
		cout<<"base fun2!\n";
	}
	virtual void fun3(){
		cout<<"base fun3!\n";
	}

	int a;
};

class Child:public Base
{
public:
	void fun1(){
		cout<<"Child fun1\n";
	}
	void fun2(){
		cout<<"Child fun2\n";
	}
	virtual void fun4(){
		cout<<"Child fun4\n";
	}
};


int _tmain(int argc, _TCHAR* argv[])
{
	Base* p=new Child;
	p->fun1();
	p->fun2();
	p->fun3();

	system("pause");
	return 0;
}

運(yùn)行結(jié)果:

結(jié)合上面的內(nèi)存布局:

其實(shí),在new Child時(shí)構(gòu)造了一個(gè)子類的對象,子類對象按上面所講,在構(gòu)造函數(shù)期間完成虛函數(shù)表指針vfptr指向Child類的虛函數(shù)表,將這個(gè)對象的地址賦值給了Base類型的指針p,當(dāng)調(diào)用p->fun1()時(shí),發(fā)現(xiàn)是虛函數(shù),調(diào)用虛函數(shù)指針查找虛函數(shù)表中對應(yīng)虛函數(shù)的地址,這里就是&Child::fun1。調(diào)用p->fun2()情況相同。調(diào)用p->fun3()時(shí),子類并沒有重寫父類虛函數(shù),但依舊通過調(diào)用虛函數(shù)指針查找虛函數(shù)表,發(fā)現(xiàn)對應(yīng)函數(shù)地址是&Base::fun3。所以上面的運(yùn)行結(jié)果如上圖所示。

到這里,你是否已經(jīng)明白為什么指向子類實(shí)例的基類指針可以調(diào)用子類(虛)函數(shù)?每一個(gè)實(shí)例對象中都存在一個(gè)vfptr指針,編譯器會(huì)先取出vfptr的值,這個(gè)值就是虛函數(shù)表vftable的地址,再根據(jù)這個(gè)值來到vftable中調(diào)用目標(biāo)函數(shù)。所以,只要vfptr不同,指向的虛函數(shù)表vftable就不同,而不同的虛函數(shù)表中存放著對應(yīng)類的虛函數(shù)地址,這樣就實(shí)現(xiàn)了多態(tài)的”效果“。

以上就是詳解C++虛函數(shù)的工作原理的詳細(xì)內(nèi)容,更多關(guān)于C++虛函數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Linux/C++多線程實(shí)例學(xué)習(xí)十字路口車輛調(diào)度

    Linux/C++多線程實(shí)例學(xué)習(xí)十字路口車輛調(diào)度

    這篇文章主要為大家介紹了Linux/C++多線程實(shí)例學(xué)習(xí)十字路口車輛調(diào)度示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • 淺談VC中預(yù)編譯的頭文件放那里的問題分析

    淺談VC中預(yù)編譯的頭文件放那里的問題分析

    本篇文章是對VC中預(yù)編譯的頭文件放那里的問題進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C++ 動(dòng)態(tài)內(nèi)存管理詳情解說

    C++ 動(dòng)態(tài)內(nèi)存管理詳情解說

    這篇文章主要介紹了C++ 動(dòng)態(tài)內(nèi)存管理詳情解說,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下,希望對你的學(xué)習(xí)有所幫助
    2022-07-07
  • FFmpeg進(jìn)階教程之給視頻添加文字水印

    FFmpeg進(jìn)階教程之給視頻添加文字水印

    FFmpeg是一套可以用來記錄、轉(zhuǎn)換數(shù)字音頻、視頻,并能將其轉(zhuǎn)化為流的開源計(jì)算機(jī)程序,下面這篇文章主要給大家介紹了關(guān)于FFmpeg進(jìn)階教程之給視頻添加文字水印的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • C++ find函數(shù)及用法小結(jié)

    C++ find函數(shù)及用法小結(jié)

    string類的find()函數(shù)用于在字符串中查找字符或子串,返回第一個(gè)匹配的位置,C++中的find()函數(shù)有多種用法,本文通過實(shí)例代碼給大家詳細(xì)講解,感興趣的朋友一起看看吧
    2023-12-12
  • C語言結(jié)構(gòu)體中內(nèi)存對齊的問題理解

    C語言結(jié)構(gòu)體中內(nèi)存對齊的問題理解

    內(nèi)存對齊”應(yīng)該是編譯器的“管轄范圍”。編譯器為程序中的每個(gè)“數(shù)據(jù)單元”安排在適當(dāng)?shù)奈恢蒙?。但是C語言的一個(gè)特點(diǎn)就是太靈活,太強(qiáng)大,它允許你干預(yù)“內(nèi)存對齊”。如果你想了解更加底層的秘密,“內(nèi)存對齊”對你就不應(yīng)該再模糊了
    2022-02-02
  • Cocos2d-x中CCEditBox文本輸入框的使用實(shí)例

    Cocos2d-x中CCEditBox文本輸入框的使用實(shí)例

    這篇文章主要介紹了Cocos2d-x中CCEditBox文本輸入框的使用實(shí)例,本文在代碼中用大量注釋講解了CCEditBox的使用方法,需要的朋友可以參考下
    2014-09-09
  • C語言宏函數(shù)container of()簡介

    C語言宏函數(shù)container of()簡介

    這篇文章介紹了C語言宏函數(shù)container of(),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-12-12
  • C語言字符函數(shù)中的isalnum()和iscntrl()你都知道嗎

    C語言字符函數(shù)中的isalnum()和iscntrl()你都知道嗎

    這篇文章主要為大家詳細(xì)介紹了C語言字符函數(shù)中的isalnum()和iscntrl(),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C語言實(shí)現(xiàn)推箱子游戲

    C語言實(shí)現(xiàn)推箱子游戲

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)推箱子游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11

最新評論

教育| 永胜县| 华池县| 盖州市| 德惠市| 额济纳旗| 井冈山市| 东平县| 沭阳县| 循化| 庐江县| 鄂托克前旗| 同德县| 沧源| 甘孜| 中西区| 雷山县| 万宁市| 涟源市| 山西省| 兴文县| 浮山县| 南岸区| 新巴尔虎左旗| 藁城市| 贞丰县| 宁海县| 前郭尔| 随州市| 喀喇| 峨眉山市| 昌图县| 湖南省| 呼和浩特市| 新津县| 乌鲁木齐市| 镇远县| 霍城县| 来凤县| 富民县| 射阳县|