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

C++中的繼承問(wèn)題(繼承基本概念、菱形虛擬繼承的對(duì)象模型)

 更新時(shí)間:2023年02月05日 11:18:35   作者:安河橋畔  
這篇文章主要介紹了C++中的繼承問(wèn)題(繼承基本概念、菱形虛擬繼承的對(duì)象模型),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

一、繼承的概念與定義格式

概念及定義格式

繼承機(jī)制是面向?qū)ο蟪绦蛟O(shè)計(jì)使代碼可以復(fù)用的最重要手段,它允許程序員在保留原有類(lèi)特性的基礎(chǔ)上進(jìn)行擴(kuò)展,增加功能,這樣產(chǎn)生的類(lèi),稱(chēng)為派生類(lèi)。繼承呈現(xiàn)了面向?qū)ο蟪绦蛟O(shè)計(jì)的層次結(jié)構(gòu),體現(xiàn)了由簡(jiǎn)單到復(fù)雜的認(rèn)知過(guò)程。繼承是類(lèi)設(shè)計(jì)層次的復(fù)用。

個(gè)人理解:父類(lèi)實(shí)際上是抽取類(lèi)的共性,將其它類(lèi)都有的屬性和方法進(jìn)行提取,再定義其它類(lèi)時(shí)只需要繼承父類(lèi),并寫(xiě)出該類(lèi)獨(dú)有的屬性即可。

以Person類(lèi)為父類(lèi),Student類(lèi)為學(xué)生類(lèi)舉例:

父類(lèi)

子類(lèi)

這里Student類(lèi)繼承了Person類(lèi),學(xué)生中就包含Person類(lèi)中的name和age兩個(gè)屬性,只需要再寫(xiě)出Student類(lèi)獨(dú)有的num屬性即可。

訪(fǎng)問(wèn)限定符與繼承權(quán)限

一句話(huà)總結(jié)上面的表格:繼承權(quán)限決定了子類(lèi)能繼承的父類(lèi)的最高權(quán)限。即public繼承不會(huì)改變類(lèi)成員的訪(fǎng)問(wèn)權(quán)限;protected繼承方式會(huì)改變?cè)瓉?lái)訪(fǎng)問(wèn)權(quán)限為public的成員;private繼承方式會(huì)影響原來(lái)訪(fǎng)問(wèn)權(quán)限為public和protected的成員。

另外還有幾點(diǎn)要注意:

父類(lèi)的private成員被子類(lèi)繼承了,但是子類(lèi)不能訪(fǎng)問(wèn)父類(lèi)的private成員,通過(guò)查看子類(lèi)的大小可以得知,子類(lèi)中包含繼承自父類(lèi)的私有成員變量。

在子類(lèi)中訪(fǎng)問(wèn)父類(lèi)私有成員會(huì)報(bào)錯(cuò):

查看子類(lèi)大小:

  • protected成員訪(fǎng)問(wèn)限定符只因?yàn)槔^承體系才出現(xiàn)的,因?yàn)閜rotected在繼承中才有意義
  • 實(shí)際中一般使用public繼承
  • 使用關(guān)鍵字class默認(rèn)的繼承方式是private,使用struct默認(rèn)的繼承方式是public,一般最好顯式給出繼承權(quán)限。

ps: class和struct的區(qū)別

  • 定義類(lèi)的默認(rèn)訪(fǎng)問(wèn)權(quán)限不同,class為私有,struct為公有,兼容C語(yǔ)言
  • 模板參數(shù)列表中可以使用class,不能使用struct
  • 繼承中的默認(rèn)繼承權(quán)限不同,class默認(rèn)private,struct默認(rèn)public

二、賦值兼容規(guī)則

這里的復(fù)制兼容規(guī)則是在public繼承的前提下:

  • 可以使用子類(lèi)對(duì)象給父類(lèi)對(duì)象賦值賦值,但是不能使用父類(lèi)對(duì)象給子類(lèi)對(duì)象賦值
  • 可以使用父類(lèi)指針指向子類(lèi)對(duì)象,但不能使用子類(lèi)指針指向父類(lèi)對(duì)象,如果一定要指向,進(jìn)行強(qiáng)制類(lèi)型轉(zhuǎn)換后可以,但是會(huì)有指針越界訪(fǎng)問(wèn)的問(wèn)題。
  • 可以使用父類(lèi)的引用去引用子類(lèi),不能使用子類(lèi)的引用引用父類(lèi),與指針原理相同。

仍以 Person類(lèi)和Student類(lèi)舉例:

Person類(lèi):

lass Person
{
protected:
	string _name;
	int _age ;
};

Student類(lèi)繼承Person類(lèi):

class Student :public Person
{
protected:
	int _num = 1;
};

分別驗(yàn)證賦值、指針和引用:

原理如圖:

指針和引用原理與上圖相同,父類(lèi)的指針可以指向子類(lèi)中繼承自父類(lèi)的部分;但是子類(lèi)的指針如果指向父類(lèi),訪(fǎng)問(wèn)_name和_age時(shí)不會(huì)有問(wèn)題,訪(fǎng)問(wèn)到_num時(shí)就會(huì)超出父類(lèi)對(duì)象的范圍,越界訪(fǎng)問(wèn),所以編譯器禁止了子類(lèi)指針指向父類(lèi)對(duì)象。

三、繼承中的作用域

  • 在繼承體系中,父類(lèi)和子類(lèi)都有獨(dú)立的作用域
  • 如果父類(lèi)和子類(lèi)中有同名成員,子類(lèi)成員會(huì)屏蔽對(duì)父類(lèi)同名成員的直接訪(fǎng)問(wèn),優(yōu)先訪(fǎng)問(wèn)自己類(lèi)中的成員,即同名隱藏,也叫重定義。
  • 對(duì)于成員函數(shù),只要函數(shù)名相同就構(gòu)成重定義,與類(lèi)型無(wú)關(guān)。

Person類(lèi):

class Person
{
public:
	void Print()
	{
		cout << "Person name:" << _name << endl;
		cout << "Person age" << _age << endl;
	}
protected:
	string _name = "ZS";
	int _age = 17;
};

Student類(lèi)繼承Person類(lèi):

class Student :public Person
{
public:
	void Print()
	{
		cout << "Student name:" << _name << endl;
		cout << "Student age:" << _age << endl;
		cout << "Student num:" << _num << endl;
	}
protected:
	string _name = "LS";
	int _age = 18;
	int _num = 2;
};

驗(yàn)證結(jié)果:

當(dāng)不加作用域限定符時(shí),子類(lèi)對(duì)象會(huì)優(yōu)先訪(fǎng)問(wèn)自己的成員變量和成員函數(shù)。

對(duì)程序稍作修改:

這里兩個(gè)Print函數(shù)的參數(shù)不同,看起來(lái)像“重載”,但是實(shí)際上是同名隱藏,子類(lèi)中對(duì)父類(lèi)的Print函數(shù)進(jìn)行了重定義。

四、子類(lèi)的默認(rèn)成員函數(shù)

構(gòu)造函數(shù)

父類(lèi) 沒(méi)有顯式定義構(gòu)造函數(shù) 或者父類(lèi)有 全缺省的構(gòu)造函數(shù) 或者 無(wú)參的構(gòu)造函數(shù) ,子類(lèi)可以不定義構(gòu)造函數(shù)。

即下面三種情況,子類(lèi)都可以不顯式地給出構(gòu)造函數(shù):

但是如果父類(lèi)顯式定義了構(gòu)造函數(shù),且不是無(wú)參或者全缺省的,子類(lèi)必須顯式定義構(gòu)造函數(shù),并在初始化列表顯式調(diào)用父類(lèi)的構(gòu)造函數(shù),因?yàn)槿绻伙@式定義,編譯器會(huì)自動(dòng)調(diào)用父類(lèi)默認(rèn)拷貝構(gòu)造函數(shù),而父類(lèi)沒(méi)有默認(rèn)的構(gòu)造函數(shù),便會(huì)報(bào)錯(cuò):

正確的寫(xiě)法:

這里的name是傳遞給Person類(lèi)構(gòu)造函數(shù)的實(shí)參,即:用name給Student對(duì)象中繼承的_name賦值。

構(gòu)造一個(gè)Student類(lèi)的對(duì)象分兩步:

  • 將從父類(lèi)繼承的成員初始化
  • 將子類(lèi)新增加的成員初始化

拷貝構(gòu)造函數(shù)

子類(lèi)的拷貝構(gòu)造函數(shù)必須在初始化列表中顯式調(diào)用父類(lèi)的拷貝構(gòu)造函數(shù)。

父類(lèi)沒(méi)有定義拷貝構(gòu)造函數(shù),子類(lèi)可以定義也可以不定義;父類(lèi)如果定義了拷貝構(gòu)造函數(shù),子類(lèi)一般要定義,并且要在初始化列表中調(diào)用父類(lèi)的拷貝構(gòu)造函數(shù)完成從父類(lèi)繼承的成員的拷貝初始化,否則會(huì)報(bào)錯(cuò):

正確寫(xiě)法:

此處s是傳遞給拷貝構(gòu)造函數(shù)的參數(shù)。

賦值運(yùn)算符重載

子類(lèi)的賦值運(yùn)算符重載函數(shù)必須調(diào)用父類(lèi)的賦值運(yùn)算符重載完成對(duì)父類(lèi)的賦值。

父類(lèi)的賦值運(yùn)算符重載:

子類(lèi):

析構(gòu)函數(shù)

子類(lèi)析構(gòu)函數(shù)會(huì)在被調(diào)用完后自動(dòng)調(diào)用父類(lèi)的析構(gòu)函數(shù)完成清理父類(lèi)成員,所以清理順序是:先清理子類(lèi),再清理父類(lèi)。

構(gòu)造和析構(gòu)函數(shù)調(diào)用順序

構(gòu)造子類(lèi)對(duì)象時(shí),先調(diào)用父類(lèi)的構(gòu)造函數(shù),再調(diào)用子類(lèi)的構(gòu)造函數(shù),清理對(duì)象時(shí),先調(diào)用子類(lèi)的析構(gòu)函數(shù),再調(diào)用父類(lèi)的析構(gòu)函數(shù)

如圖:

因?yàn)闃?gòu)造子類(lèi)對(duì)象時(shí)會(huì)在初始化列表中調(diào)用父類(lèi)的構(gòu)造函數(shù),執(zhí)行完之后才會(huì)執(zhí)行子類(lèi)的構(gòu)造函數(shù)的函數(shù)體,所以父類(lèi)的構(gòu)造會(huì)先于子類(lèi)的構(gòu)造執(zhí)行。

五、繼承與友元、靜態(tài)成員

友元關(guān)系

友元關(guān)系不能繼承

tips:王叔是你父親的好朋友,但是不一定是你的好朋友,王叔的財(cái)產(chǎn)不會(huì) 給你繼承??

定義一個(gè)Display函數(shù),并在Person類(lèi)中聲明為友元類(lèi):

在Display函數(shù)中可以訪(fǎng)問(wèn)Person類(lèi)的protected成員,但是不能訪(fǎng)問(wèn)其子類(lèi)Student類(lèi)成員,友元關(guān)系不能繼承。

靜態(tài)成員

父類(lèi)中聲明了static靜態(tài)成員,則整個(gè)繼承體系只有一個(gè)這樣的成員。無(wú)論派生出多少子類(lèi),都只有一個(gè)static成員實(shí)例。

定義A、B、C三個(gè)類(lèi):

class A
{
protected:
	int _a;
public:
	static int _count;//類(lèi)中聲明為靜態(tài)成員
};

int A::_count = 0;//類(lèi)外定義
class B:public A
{
protected:
	int _b;
};
class C :public B
{
protected:
	int _c;
};

通過(guò)不同對(duì)象訪(fǎng)問(wèn)_count:

六、菱形繼承及菱形虛擬繼承

菱形繼承概念

單繼承

多繼承

菱形繼承

可以看出,菱形繼承實(shí)際就是單繼承和多繼承組合的結(jié)果,是多繼承的一種特殊情況。

菱形繼承實(shí)例:

注:驗(yàn)證環(huán)境為VS2022,win32平臺(tái)

這里C類(lèi)的大小是20字節(jié),除了其本身成員的4字節(jié),另外16個(gè)字節(jié)都是從兩個(gè)父類(lèi)繼承來(lái)的。模型如圖:

存在問(wèn)題

對(duì)于上面圖中的菱形繼承,存在的問(wèn)題十分明顯,那就是數(shù)據(jù)冗余二義性問(wèn)題。即Teacher類(lèi)和Student類(lèi)都繼承自Person類(lèi),那么兩個(gè)類(lèi)中都會(huì)包含Person類(lèi)中的成員,Assistant繼承這兩個(gè)類(lèi)之后,同樣的成員便會(huì)包含兩份,導(dǎo)致數(shù)據(jù)重復(fù),并且在通過(guò)Assistant對(duì)象訪(fǎng)問(wèn)Person類(lèi)中的成員時(shí),會(huì)有二義性。

通過(guò)添加作用域限定符可以解決訪(fǎng)問(wèn)二義性的問(wèn)題,如:as.Teacher::_name類(lèi)似的語(yǔ)句可以指定通過(guò)哪個(gè)父類(lèi)訪(fǎng)問(wèn)Person類(lèi)的對(duì)象,但是無(wú)法從根本解決數(shù)據(jù)冗余的問(wèn)題,所以便引入了虛擬繼承的概念。

虛擬繼承的概念

虛擬繼承是指在繼承權(quán)限前面加上一個(gè)virtura關(guān)鍵字

class B1:virtual public A
{
public:
	int _b1;
};

虛擬繼承可以解決菱形繼承的二義性和數(shù)據(jù)冗余的問(wèn)題。對(duì)于上面的菱形繼承,在B1和B2繼承A時(shí)使用虛擬繼承即可解決問(wèn)題。

虛擬繼承的模型

對(duì)于上面的菱形虛擬繼承,研究其模型。

通過(guò)sizeof打印輸出獲取c對(duì)象的大小為24字節(jié):

通過(guò)下面的語(yǔ)句為c對(duì)象中的成員賦值:

void Test()
{
	C c;
	c._a = 1;
	c._b1 = 2;
	c._b2 = 3;
	c._c = 4;

	cout << sizeof(c) << endl;
}

查看其內(nèi)存分布

所以,菱形虛擬繼承將最上面的父類(lèi)中的成員只保存了一份,并用一個(gè)偏移量指針指向偏移量表格,偏移量表格中保存的就是最上面的父類(lèi)中的成員變量相對(duì)于當(dāng)前對(duì)象的偏移量。

最終得到的菱形虛擬繼承對(duì)象內(nèi)存模型如圖:

對(duì)象模型與偏移量表格:

總結(jié):假設(shè)B1和B2繼承自A類(lèi),最下面的C類(lèi)繼承自B1和B2類(lèi);菱形虛擬繼承是指兩個(gè)子類(lèi)繼承自同一個(gè)父類(lèi)時(shí),將繼承方式設(shè)置為虛擬繼承;構(gòu)建對(duì)象時(shí),最頂層的父類(lèi)A中的成員變量只保存一份,在對(duì)象模型的最下面,這樣就避免了數(shù)據(jù)冗余;B1和B2都有屬于自己的虛基表指針,通過(guò)虛基表中的偏移量找到最頂層父類(lèi)中的_a成員變量;

B1和B1類(lèi)中都有自己的虛表,這樣便可以通過(guò)C類(lèi)對(duì)象給B1類(lèi)和者B2類(lèi)的指針、引用或者對(duì)象賦值,滿(mǎn)足賦值兼容規(guī)則

附錄:

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • C++實(shí)現(xiàn)簡(jiǎn)單學(xué)生信息管理系統(tǒng)

    C++實(shí)現(xiàn)簡(jiǎn)單學(xué)生信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡(jiǎn)單學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • c++學(xué)習(xí)之構(gòu)造函數(shù)

    c++學(xué)習(xí)之構(gòu)造函數(shù)

    類(lèi)多么重要我就不多說(shuō)了,只講講學(xué)習(xí),因?yàn)閭€(gè)人認(rèn)為類(lèi)的學(xué)習(xí)無(wú)論從概念的理解還是實(shí)際代碼的編寫(xiě)相對(duì)其他C兼容向的代碼都是比較有難度的, 對(duì)于以前學(xué)C 的人來(lái)說(shuō)這才是真正的新概念和內(nèi)容,STL其實(shí)還比較好理解,不就是一個(gè)更大的函數(shù)庫(kù)和代碼可以使用嘛。
    2015-06-06
  • c++標(biāo)準(zhǔn)庫(kù)讀寫(xiě)ini文件的實(shí)現(xiàn)示例

    c++標(biāo)準(zhǔn)庫(kù)讀寫(xiě)ini文件的實(shí)現(xiàn)示例

    本文介紹了一個(gè)完整的INI文件類(lèi)的實(shí)現(xiàn),包含讀取和寫(xiě)入操作,通過(guò)IniFile.h頭文件和IniFile.cpp實(shí)現(xiàn)文件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-10-10
  • 基于C++實(shí)現(xiàn)kinect+opencv 獲取深度及彩色數(shù)據(jù)

    基于C++實(shí)現(xiàn)kinect+opencv 獲取深度及彩色數(shù)據(jù)

    本文的主要思想是Kinect SDK 讀取彩色、深度、骨骼信息并用OpenCV顯示,非常的實(shí)用,有需要的小伙伴可以參考下
    2015-12-12
  • Qt串口通信開(kāi)發(fā)之QSerialPort模塊簡(jiǎn)單使用方法與實(shí)例

    Qt串口通信開(kāi)發(fā)之QSerialPort模塊簡(jiǎn)單使用方法與實(shí)例

    這篇文章主要介紹了Qt串口通信開(kāi)發(fā)之QSerialPort模塊簡(jiǎn)單使用方法與實(shí)例,需要的朋友可以參考下
    2020-03-03
  • Qt消除警告的實(shí)現(xiàn)示例

    Qt消除警告的實(shí)現(xiàn)示例

    Qt5 和 Qt6 之間存在一些差異,導(dǎo)致在編譯時(shí)可能產(chǎn)生警告,為了消除這些警告,Qt 提供了一些宏定義來(lái)幫助你在代碼中處理這些差異,本文主要介紹了Qt消除警告的實(shí)現(xiàn)示例,感興趣的可以了解一下
    2023-09-09
  • C語(yǔ)言函數(shù)指針與回調(diào)函數(shù)的實(shí)現(xiàn)

    C語(yǔ)言函數(shù)指針與回調(diào)函數(shù)的實(shí)現(xiàn)

    本文主要介紹了C語(yǔ)言函數(shù)指針與回調(diào)函數(shù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • C語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)快速排序

    C語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)快速排序

    快速排序是一種不穩(wěn)定排序,這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言簡(jiǎn)單實(shí)現(xiàn)快速排序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-01-01
  • 淺析C++的引用與const指針與各種傳遞方式

    淺析C++的引用與const指針與各種傳遞方式

    這篇文章主要介紹了淺析C++的引用與const指針與各種傳遞方式的相關(guān)資料,需要的朋友可以參考下
    2017-08-08
  • Qt使用SQLite數(shù)據(jù)庫(kù)實(shí)現(xiàn)數(shù)據(jù)增刪改查

    Qt使用SQLite數(shù)據(jù)庫(kù)實(shí)現(xiàn)數(shù)據(jù)增刪改查

    這篇文章主要為大家詳細(xì)介紹了Qt如何使用SQLite數(shù)據(jù)庫(kù)實(shí)現(xiàn)數(shù)據(jù)增刪改查功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下
    2023-06-06

最新評(píng)論

威海市| 甘德县| 改则县| 丰城市| 新乡县| 独山县| 沈阳市| 农安县| 称多县| 兴仁县| 堆龙德庆县| 锡林浩特市| 宜昌市| 临洮县| 连山| 安多县| 泰来县| 泉州市| 韶关市| 上高县| 孟州市| 顺平县| 日照市| 沁源县| 泸水县| 吴旗县| 嵩明县| 青河县| 抚远县| 崇州市| 承德市| 卢氏县| 清水县| 台东市| 七台河市| 吴旗县| 黄冈市| 农安县| 梨树县| 衡水市| 淳安县|