欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

概念及定義格式

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

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

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

父類

子類

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

訪問限定符與繼承權(quán)限

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

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

父類的private成員被子類繼承了,但是子類不能訪問父類的private成員,通過查看子類的大小可以得知,子類中包含繼承自父類的私有成員變量。

在子類中訪問父類私有成員會(huì)報(bào)錯(cuò):

查看子類大?。?/p>

  • protected成員訪問限定符只因?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ū)別

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

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

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

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

仍以 Person類和Student類舉例:

Person類:

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

Student類繼承Person類:

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

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

原理如圖:

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

三、繼承中的作用域

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

Person類:

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

Student類繼承Person類:

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í),子類對(duì)象會(huì)優(yōu)先訪問自己的成員變量和成員函數(shù)。

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

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

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

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

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

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

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

正確的寫法:

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

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

  • 將從父類繼承的成員初始化
  • 將子類新增加的成員初始化

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

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

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

正確寫法:

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

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

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

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

子類:

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

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

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

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

如圖:

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

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

友元關(guān)系

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

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

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

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

靜態(tài)成員

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

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

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

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

通過不同對(duì)象訪問_count:

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

菱形繼承概念

單繼承

多繼承

菱形繼承

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

菱形繼承實(shí)例:

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

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

存在問題

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

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

虛擬繼承的概念

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

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

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

虛擬繼承的模型

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

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

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

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

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

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

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

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

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

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

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

附錄:

總結(jié)

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

相關(guān)文章

最新評(píng)論