成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

深度探索C++關(guān)鍵字之virtual-創(chuàng)新互聯(lián)

virtual在C++中有兩個(gè)重要的用途:一是解決由多繼承中父類(lèi)有相同基類(lèi)引起的子類(lèi)中成員的二義性問(wèn)題,二是實(shí)現(xiàn)多態(tài)。

創(chuàng)新互聯(lián)一直秉承“誠(chéng)信做人,踏實(shí)做事”的原則,不欺瞞客戶(hù),是我們最起碼的底線(xiàn)! 以服務(wù)為基礎(chǔ),以質(zhì)量求生存,以技術(shù)求發(fā)展,成交一個(gè)客戶(hù)多一個(gè)朋友!為您提供網(wǎng)站制作、做網(wǎng)站、成都網(wǎng)頁(yè)設(shè)計(jì)、微信小程序定制開(kāi)發(fā)、成都網(wǎng)站開(kāi)發(fā)、成都網(wǎng)站制作、成都軟件開(kāi)發(fā)、App定制開(kāi)發(fā)是成都本地專(zhuān)業(yè)的網(wǎng)站建設(shè)和網(wǎng)站設(shè)計(jì)公司,等你一起來(lái)見(jiàn)證!

一、解決二義性

1、引起二義性的原因

    二義性是在多繼承中出現(xiàn)的,如果派生類(lèi)的父類(lèi)繼承了同一個(gè)基類(lèi),那么派生類(lèi)對(duì)象訪(fǎng)問(wèn)繼承自基類(lèi)中成員時(shí)便會(huì)出現(xiàn)二義性。如下代碼:

#include <iostream>
#include <cstdlib>

class Base
{
public:
	int _b;
};

class Base1: public Base
{
public:
	int _b1;
};

class Base2: public Base
{
public:
	int _b1;
};

class Deriver : public Base1, public Base2
{
public:
	int _d;
};

int main()
{
	Deriver d;
	//d._b=

	system("pause");
	return 0;
}

深度探索C++關(guān)鍵字之 virtual

   派生類(lèi)Derive的父類(lèi)Base1和Base2都繼承了同一個(gè)基類(lèi)Base,在派生類(lèi)Derive中,Base的成員_b被繼承了兩次,在訪(fǎng)問(wèn)成員_b時(shí)會(huì)出現(xiàn)二義性,因?yàn)闊o(wú)法確定訪(fǎng)問(wèn)繼承自Base1中的_b還是訪(fǎng)問(wèn)繼承自Base2中的_b。

2、引入虛擬繼承,解決二義性問(wèn)題

    讓Base1和Base2虛擬繼承Base,其他代碼不變,就可以解決Deriver中_b的二義性問(wèn)題。代碼如下:

class Base1: virtual public Base
{
public:
	int _b1;
};

class Base2: virtual public Base
{
public:
	int _b1;
};

    Base1和Base2虛擬繼承Base后的模型如下:

深度探索C++關(guān)鍵字之 virtual

    由上圖可以看出,當(dāng)Base1虛擬繼承Base后,Base1前四字節(jié)存放了一個(gè)地址,通過(guò)這個(gè)地址可以找到Base中成員相對(duì)于存放這個(gè)地址空間的偏移,進(jìn)而找到Base中的成員。

    當(dāng)Deriver繼承了Base1和Base2后,Deriver的模型如下:

深度探索C++關(guān)鍵字之 virtual

    當(dāng)Base1和Base2虛擬繼承了Base后,Deriver繼承Base1和Base2就不會(huì)出現(xiàn)二義性的問(wèn)題。因?yàn)锽ase的成員_d在Deriver中只保留了一份,而B(niǎo)ase1和Base2中本來(lái)屬于_b的空間被放了兩個(gè)地址,通過(guò)這個(gè)地址可以找到_b相對(duì)于當(dāng)前位置的偏移,進(jìn)而找到_b。

    ▲當(dāng)一個(gè)基類(lèi)由多個(gè)派生類(lèi)時(shí),這些派生類(lèi)在繼承基類(lèi)時(shí)最好使用虛擬繼承,以防某個(gè)類(lèi)繼承多個(gè)這些派生類(lèi)時(shí),產(chǎn)生二義性。

3、深度探索virtual繼承模型

    在學(xué)習(xí)這部分知識(shí)時(shí),一起學(xué)學(xué)習(xí)的同學(xué)問(wèn)了我一個(gè)問(wèn)題,在菱形繼承中(也就是上舉的的例子),在哪要虛擬繼承?在Deriver繼承Base1和Base2時(shí)要不要虛擬繼承?只在Deriver繼承Base1和Base2時(shí)虛擬繼承行不行? 當(dāng)時(shí)學(xué)藝不精,把我也難住了,我只知道Base1和Base2虛擬繼承Base就可以解決二義性問(wèn)題,個(gè)中細(xì)節(jié)并不是很清楚,于是就在VS2013和vc 6.0環(huán)境下研究了一下虛擬繼承的模型。

    我們知道在直接繼承中,先繼承的放在前面,后繼承的放在后面,最后才是派生類(lèi)中新增的成員。在本文開(kāi)始的部分,存在二義性的那個(gè)例子就是這樣的。

    那么在虛擬繼承中有什么規(guī)律呢?下面的規(guī)律是我測(cè)試了許多例子后得出的,如有錯(cuò)誤還望指正。

規(guī)律一:如果一個(gè)派生類(lèi)虛擬繼承一個(gè)基類(lèi),那么派生類(lèi)的模型如下:

深度探索C++關(guān)鍵字之 virtual

  也就是說(shuō),只要有虛擬繼承,派生類(lèi)模型最開(kāi)始的部分必然是一個(gè)地址,這個(gè)地址間接指向虛擬繼承自基類(lèi)的部分。然后是派生類(lèi)定義的成員,最后是虛擬繼承自基類(lèi)的部分。

規(guī)律二:如果一個(gè)派生類(lèi)虛擬繼承了兩個(gè)基類(lèi),那么派生類(lèi)的模型如下:

深度探索C++關(guān)鍵字之 virtual

    如上圖,無(wú)論派生類(lèi)虛擬繼承多少個(gè)基類(lèi),在開(kāi)始的部分只有一個(gè)地址,指向所有虛擬繼承自基類(lèi)的開(kāi)始,虛擬繼承自基類(lèi)的部分放在一起,先繼承的放在前面,后繼承的放在后面。

規(guī)律三:在多繼承中,只要有虛擬繼承不論有無(wú)直接繼承,派生類(lèi)模型開(kāi)始的四個(gè)字節(jié)必然是一個(gè)地址,這個(gè)地址間接指向虛擬繼承自基類(lèi)的開(kāi)始部分。

規(guī)律四:如果在多繼承中既有直接繼承又有虛擬繼承,無(wú)論先是直接繼承還是先是虛擬繼承,在滿(mǎn)足規(guī)律三的前提下,存放的順序依次是直接繼承的部分、派生類(lèi)固有成員、虛擬繼承基類(lèi)的部分。如下圖所示:

深度探索C++關(guān)鍵字之 virtual

    在上面的繼承中直接繼承部分,先繼承的放在前面,后繼承的放在后面。虛擬繼承部分同理。

規(guī)律五:虛擬繼承部分永遠(yuǎn)放在直接繼承部分和派生類(lèi)定義的成員部分后面,其中虛擬繼承部分中,直接繼承類(lèi)中的虛擬繼承部分放在虛擬繼承部分的最前面。如下圖所示:

深度探索C++關(guān)鍵字之 virtual

二、virtual實(shí)現(xiàn)多態(tài)

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

    多態(tài)即多種狀態(tài),我的理解是,具有不同功能的函數(shù)可以用同一個(gè)函數(shù)名,這樣就可以用同一個(gè)函數(shù)名調(diào)用不同功能的函數(shù),比如函數(shù)重載。

    多態(tài)分為靜態(tài)多態(tài)如函數(shù)重載,和動(dòng)態(tài)多態(tài)。動(dòng)態(tài)多態(tài)是通過(guò)虛函數(shù)來(lái)實(shí)現(xiàn)的。

2、virtual函數(shù)實(shí)現(xiàn)多態(tài)

    如果在基類(lèi)定義一個(gè)虛函數(shù),這個(gè)虛函數(shù)允許在派生類(lèi)中重寫(xiě)[virtual 函數(shù)名相同,參數(shù)類(lèi)型相同,返回值類(lèi)型相同(協(xié)變除外)注:函數(shù)重載、重寫(xiě)、覆蓋的區(qū)別見(jiàn)附錄]與基類(lèi)同名的函數(shù),并且可以通過(guò)基類(lèi)的指針或引用訪(fǎng)問(wèn)基類(lèi)和派生類(lèi)中的同名函數(shù)。這就是多態(tài)的實(shí)現(xiàn)。

    代碼如下:

#include <iostream>
#include <cstdlib>

using namespace std;

class Base
{
public:
virtual void Display()
{
cout << "Base::Display()" << endl;
}
};

class Deriver :public Base
{
public:
virtual void Display()
{
cout << "Deriver::Display()" << endl;
}

};

void FunTest()
{
Base b;
Base* pb = &b;
pb->Display();

        Deriver d;
pb = &d;
pb->Display();
}

int main()
{

FunTest();

system("pause");
return 0;
}

    上面的代碼輸出結(jié)果如下:

深度探索C++關(guān)鍵字之 virtual

    多態(tài)能夠?qū)崿F(xiàn)除了虛函數(shù)外,還有一個(gè)重要的原因是基類(lèi)對(duì)象的指針可以指向或者引用派生類(lèi)的對(duì)象。

    派生類(lèi)繼承了基類(lèi),用基類(lèi)指針指向或者引用派生類(lèi)時(shí),就會(huì)找到派生類(lèi)繼承基類(lèi)的部分。反則,派生類(lèi)的指針不能指向或者引用基類(lèi)的對(duì)象,這是因?yàn)槿绻@樣可以指向的話(huà),就會(huì)發(fā)生越界訪(fǎng)問(wèn)的情況。

3、深度探索多態(tài)的實(shí)現(xiàn)

    上面說(shuō)只要在基類(lèi)中定義虛函數(shù),在派生類(lèi)重寫(xiě)虛函數(shù),然后就可以用基類(lèi)的指針或者引用調(diào)用基類(lèi)或者派生類(lèi)的虛函數(shù),那么為什么這樣能夠?qū)崿F(xiàn)呢?

    在基類(lèi)定義虛函數(shù)后,基類(lèi)就會(huì)產(chǎn)生一個(gè)地址_vfptr,這個(gè)地址指向的地方存放著所有虛函數(shù)的入口地址,以00 00 00 00即NULL結(jié)束。 派生類(lèi)直接繼承這個(gè)基類(lèi)時(shí),會(huì)把這個(gè)地址也繼承過(guò)去。如果派生類(lèi)重寫(xiě)繼承來(lái)的虛函數(shù),那么這個(gè)虛函數(shù)在虛表中的入口地址就會(huì)更新為重寫(xiě)的虛函數(shù)的地址,那么用基類(lèi)的引用或者指針指向派生類(lèi)時(shí),調(diào)用這個(gè)虛函數(shù)時(shí)就會(huì)調(diào)用派生類(lèi)重寫(xiě)的虛函數(shù)。

#include <iostream>
#include <cstdlib>

using namespace std;

class Base
{
public:
	void f()
	{
		cout << "Base:: f()" << endl;
	}

	virtual void g()
	{
		cout << "Base:: g()" << endl;
	}

	virtual void h()
	{
		cout << "Base:: h()" << endl;
	}

	int _b;
};

class Deriver :public Base
{
public:
	virtual void g()
	{
		cout << "Deriver:: g()" << endl;
	}

	int _d;
};


int main()
{
	Base b;
	Deriver d;
	Base* pb = &b;
	pb->f();
	pb->g();
	pb->h();

	pb = &d;
	pb->f();
	pb->g();
	pb->h();

	system("pause");
	return 0;
}

深度探索C++關(guān)鍵字之 virtual

    上面的代碼中,在基類(lèi)定義了一個(gè)普通函數(shù) f() 和兩個(gè)虛函數(shù) g() 、h() ,在派生類(lèi)只對(duì)g() 進(jìn)行了重寫(xiě)。然后用基類(lèi)的指針指向基類(lèi)和派生類(lèi)來(lái)調(diào)用這些函數(shù),結(jié)果如上圖所示。

    Base和Derive的模型如下:

深度探索C++關(guān)鍵字之 virtual

    派生類(lèi)在繼承基類(lèi)時(shí)也把基類(lèi)的虛表也繼承了,當(dāng)用基類(lèi)的指針指向派生類(lèi)后,調(diào)用虛函數(shù)時(shí),會(huì)找到虛表進(jìn)而找到虛函數(shù),所以在派生類(lèi)對(duì)g() 重寫(xiě)后,調(diào)用的就是重寫(xiě)的函數(shù),因?yàn)橹貙?xiě)更新了虛表。至于f()不是虛函數(shù),但是派生類(lèi)繼承了它當(dāng)然可以調(diào)用它,只不過(guò)調(diào)用的是基類(lèi)的函數(shù)。

4、深度探索虛表的模型

    在VS2013 和vc6.0中 :

 4.1 虛表地址(_vfptr)的位置

    (1)如果一個(gè)基類(lèi)定義了虛函數(shù),那么_vfptr 的位置在這個(gè)基類(lèi)的最前面4個(gè)字節(jié)。如果基類(lèi)虛擬繼承了其他的基類(lèi),那么_vfptr的位置在指向偏移量的那個(gè)地址之后,即第二個(gè)四字節(jié)的位置

    (2)在直接繼承中,有多少個(gè)含有虛函數(shù)的基類(lèi)就有多少個(gè)_vfptr, _vfptr的位置滿(mǎn)足(1)中所述

    (3)在只有虛擬繼承中,有多少個(gè)含有虛函數(shù)的基類(lèi)就有多少個(gè)_vfptr,如果派生類(lèi)定義新的虛函數(shù),那么就會(huì)再產(chǎn)生一個(gè)派生類(lèi)的_vfptr。_vfptr的位置在  指向虛基類(lèi)的偏移量的地址 之后。

    (4)在既有直接繼承又有虛擬繼承,如果直接繼承至少有一個(gè)有虛函數(shù),那么,有多少個(gè)含有虛函數(shù)的基類(lèi)就有多少個(gè)_vfptr, _vfptr的位置滿(mǎn)足(1)中所述。,如果直接繼承的基類(lèi)沒(méi)有虛函數(shù),那么滿(mǎn)足(3)中所述。

4.2 虛表模型

    派生類(lèi)在繼承基類(lèi)的同時(shí),也繼承了基類(lèi)的虛表。

    注意:繼承的時(shí)候并不是把基類(lèi)的虛表的地址直接拿來(lái),而是拷貝了一份虛表,虛表的地址不同,但是內(nèi)容一樣的。

    通過(guò)_vfptr就可以找到虛表,虛表在填寫(xiě)的時(shí)候遵循一定的規(guī)律,如下:

    (1)在基類(lèi)中定義虛函數(shù),先定義的虛函數(shù)在前,后定義的虛函數(shù)在后,以NULL結(jié)束

    (2)在派生類(lèi)中如果重寫(xiě)了虛函數(shù),那么就會(huì)更新對(duì)應(yīng)的函數(shù)的入口地址。

    (3)派生類(lèi)新定義的虛函數(shù)數(shù)會(huì)存到第一個(gè)直接繼承的含有虛函數(shù)的基類(lèi)的虛表中,存放規(guī)則如(1)所述,如果沒(méi)有直接繼承的基類(lèi),派生類(lèi)會(huì)重新生成一個(gè)虛表,填寫(xiě)規(guī)則如(1)所述。

    注:如果繼承的多個(gè)基類(lèi)含有相同的虛函數(shù),必須在派生類(lèi)重寫(xiě),否則在用派生類(lèi) 類(lèi)型的指針調(diào)用的函數(shù)時(shí)會(huì)出現(xiàn)二義性問(wèn)題。同樣在多個(gè)基類(lèi)定義普通的函數(shù)也會(huì)出現(xiàn)二義性。解決方法很簡(jiǎn)單,就是換個(gè)函數(shù)名……

附錄:函數(shù)重載、重寫(xiě)、隱藏的區(qū)別

深度探索C++關(guān)鍵字之 virtual


另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線(xiàn),公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。

當(dāng)前文章:深度探索C++關(guān)鍵字之virtual-創(chuàng)新互聯(lián)
瀏覽路徑:http://jinyejixie.com/article40/djcseo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)網(wǎng)站策劃、服務(wù)器托管、外貿(mào)網(wǎng)站建設(shè)、面包屑導(dǎo)航、用戶(hù)體驗(yàn)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化
武威市| 宣化县| 休宁县| 垫江县| 高清| 庄浪县| 娄烦县| 汾阳市| 辽阳县| 巴林左旗| 台北市| 武胜县| 安泽县| 石嘴山市| 车险| 安阳县| 吉隆县| 榆树市| 富锦市| 社旗县| 加查县| 台东县| 讷河市| 长泰县| 衡南县| 简阳市| 朔州市| 定远县| 开原市| 柏乡县| 且末县| 田东县| 阳江市| 松潘县| 玉林市| 珠海市| 饶平县| 蛟河市| 曲水县| 公安县| 刚察县|