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

C++繼承的賦值轉(zhuǎn)換與菱形虛擬繼承深入詳解

 更新時(shí)間:2022年08月03日 10:31:11   作者:Rookiep  
今天我要給大家介紹C++中更深入的內(nèi)容了,C++繼承的賦值轉(zhuǎn)換與菱形虛擬繼承。C++這門語(yǔ)言為了使代碼不冗余,做了些什么操作呢?C++的繼承就很好地實(shí)現(xiàn)了類層次的代碼復(fù)用,今天我就要來(lái)和大家好好聊一聊它了

一、繼承的概念及定義

繼承是面向?qū)ο笕筇匦灾弧?/p>

1.1、繼承的概念

繼承(inheritance)機(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)知過(guò)程。以前我們接觸的復(fù)用都是函數(shù)復(fù)用。繼承是類設(shè)計(jì)層次的復(fù)用。

1.2、繼承的定義

繼承的語(yǔ)法:class 子類 : 繼承方式 父類

繼承方式:

  • 共有繼承
  • 私有繼承
  • 保護(hù)繼承

基類private成員在派生類中無(wú)論以什么方式繼承都是不可見的。這里的不可見是指基類的私有成員還是被繼承到了派生類對(duì)象中,但是語(yǔ)法上限制派生類對(duì)象不管在類里面還是類外面都不能去訪問(wèn)它。

二、基類和派生類對(duì)象賦值轉(zhuǎn)換

  • 派生類對(duì)象可以賦值給基類的對(duì)象/基類的指針/基類的引用。這里有個(gè)形象的說(shuō)法叫切片或者切割。寓意把派生類中父類那部分切來(lái)賦值過(guò)去。
  • 基類對(duì)象不能賦值給派生類對(duì)象。
  • 基類的指針或者引用可以通過(guò)強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針或者引用。但是必須是基類的指針是指向派生類對(duì)象時(shí)才是安全的。

class person{
protected:
	string _name;
	int _age;
};
class student :public person
{
public:
	int _No;
};
void test01()
{
	student sobj;
	//1.子類對(duì)象可以賦值給父類對(duì)象/指針/引用
	person pobj = sobj;
	person* pp = &sobj;
	person& rp = sobj;
	//2.基類對(duì)象不可以賦值給派生類對(duì)象
	//sobj = pobj;
	//3.基類的指針可以通過(guò)強(qiáng)制類型轉(zhuǎn)換賦值給派生類的指針
	pp = &sobj;
	student* ps1 = (student*)pp;//這種情況是可以的
	ps1->_No = 10;
	pp = &pobj;
	student* ps2 = (student*)pp;//這種情況轉(zhuǎn)換時(shí)雖然可以,但存在越界訪問(wèn)的問(wèn)題
	ps2->_No = 10;
}

三、繼承中的作用域

3.1、繼承同名成員處理方式

??問(wèn)題:當(dāng)子類與父類出現(xiàn)同名的成員,如何通過(guò)子類對(duì)象,訪問(wèn)到子類或父類中同名的數(shù)據(jù)呢?

class Base {
public:
	Base(){
		m_A = 100;
	}
	void func(){
		cout << "Base - func()調(diào)用" << endl;
	}
	void func(int a){
		cout << "Base - func(int a)調(diào)用" << endl;
	}
public:
	int m_A;
};
class Son : public Base {
public:
	Son(){
		m_A = 200;
	}
	//當(dāng)子類與父類擁有同名的成員函數(shù),子類會(huì)隱藏父類中所有版本的同名成員函數(shù)
	//如果想訪問(wèn)父類中被隱藏的同名成員函數(shù),需要加父類的作用域
	void func()
	{
		cout << "Son - func()調(diào)用" << endl;
	}
public:
	int m_A;
};
void test01()
{
	Son s;
	cout << "Son下的m_A = " << s.m_A << endl;
	cout << "Base下的m_A = " << s.Base::m_A << endl;
	s.func();
	s.Base::func();
	s.Base::func(10);
}

??????總結(jié):

  • 子類對(duì)象可以直接訪問(wèn)到子類中同名成員
  • 子類對(duì)象加作用域可以訪問(wèn)到父類同名成員
  • 當(dāng)子類與父類擁有同名的成員函數(shù),子類會(huì)隱藏父類中同名成員函數(shù),加作用域可以訪問(wèn)到父類中同名函數(shù)。
  • 當(dāng)然父類對(duì)象隨便調(diào)用父類成員。

注:子類和父類中有同名成員時(shí)構(gòu)成隱藏關(guān)系,也叫重定義。需要注意的是,如果是成員函數(shù)的隱藏,只需要函數(shù)名相同就構(gòu)成隱藏。

3.2、繼承同名靜態(tài)成員處理方式

??:問(wèn)題:繼承中同名的靜態(tài)成員在子類對(duì)象上如何進(jìn)行訪問(wèn)?

靜態(tài)成員和非靜態(tài)成員出現(xiàn)同名,處理方式一致:

  • 子類對(duì)象訪問(wèn)子類同名成員 直接訪問(wèn)即可
  • 子類對(duì)象訪問(wèn)父類同名成員 需要加作用域
class Base {
public:
	static void func()
	{
		cout << "Base - static void func()" << endl;
	}
	static void func(int a)
	{
		cout << "Base - static void func(int a)" << endl;
	}
	static int m_A;
};
int Base::m_A = 100;
class Son : public Base {
public:
	static void func()
	{
		cout << "Son - static void func()" << endl;
	}
	static int m_A;
};
int Son::m_A = 200;
//同名成員屬性
void test01()
{
	//通過(guò)對(duì)象訪問(wèn)
	cout << "通過(guò)對(duì)象訪問(wèn): " << endl;
	Son s;
	cout << "Son  下 m_A = " << s.m_A << endl;
	cout << "Base 下 m_A = " << s.Base::m_A << endl;
	//通過(guò)類名訪問(wèn)
	cout << "通過(guò)類名訪問(wèn): " << endl;
	cout << "Son  下 m_A = " << Son::m_A << endl;
	cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}
//同名成員函數(shù)
void test02()
{
	//通過(guò)對(duì)象訪問(wèn)
	cout << "通過(guò)對(duì)象訪問(wèn): " << endl;
	Son s;
	s.func();
	s.Base::func();
	cout << "通過(guò)類名訪問(wèn): " << endl;
	Son::func();
	Son::Base::func();
	//出現(xiàn)同名,子類會(huì)隱藏掉父類中所有同名成員函數(shù),需要加作作用域訪問(wèn)
	Son::Base::func(100);
}

總結(jié):同名靜態(tài)成員處理方式和非靜態(tài)處理方式一樣,只不過(guò)有兩種訪問(wèn)的方式(通過(guò)對(duì)象 和 通過(guò)類名)

3.3、繼承與友元

友元關(guān)系不可以繼承,也就是說(shuō)基類的友元不要可以訪問(wèn)子類的私有成員和保護(hù)成員。

(就好比說(shuō)爸爸的朋友不一定是我的朋友)

3.4、繼承與靜態(tài)成員

基類定義了static靜態(tài)成員,則整個(gè)繼承體系只有這一個(gè)成員(我們知道靜態(tài)成員是整個(gè)類共享的),無(wú)論派生出多少個(gè)子類,都只有這么一個(gè)static成員。

class person
{
public:
	person()
	{
		_count++;
	}
protected:
	string _name;
public:
	static int _count;//統(tǒng)計(jì)人數(shù)
};
int person::_count = 0;
class student:public person
{
protected:
	int _stuNum;
};
class graduate :public student
{
protected:
	string course;
};
void test()
{
	student s1;
	student s2;
	student s3;
	graduate s4;
	cout << "人數(shù)" << person::_count << endl;
	student::_count = 0;
	cout << "人數(shù)" << person::_count << endl;
}

人數(shù)4
人數(shù)0
請(qǐng)按任意鍵繼續(xù). .

代碼解釋:因?yàn)樽宇悓?duì)象構(gòu)造是會(huì)調(diào)用基類的構(gòu)造函數(shù),所以每實(shí)例化一個(gè)子類對(duì)象都會(huì)調(diào)用一次基類構(gòu)造,從而_count++,并且靜態(tài)成員是整個(gè)類共享的,所以無(wú)論哪個(gè)子類都可修改?。?!

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

6個(gè)默認(rèn)成員函數(shù),“默認(rèn)"的意思就是指我們不寫,編譯器會(huì)變我們自動(dòng)生成一個(gè),那么在派生類中,這幾個(gè)成員函數(shù)是如何生成的呢?

  1. 派生類的構(gòu)造函數(shù)必須調(diào)用基類的構(gòu)造函數(shù)初始化基類的那一部分成員。如果基類沒(méi)有默認(rèn)的構(gòu)造函數(shù),則必須在派生類構(gòu)造函數(shù)的初始化列表階段顯示調(diào)用。
  2. 派生類的拷貝構(gòu)造函數(shù)必須調(diào)用基類的拷貝構(gòu)造完成基類的拷貝初始化。
  3. 派生類的operator=必須要調(diào)用基類的operator=完成基類的復(fù)制。
  4. 派生類的析構(gòu)函數(shù)會(huì)在被調(diào)用完成后自動(dòng)調(diào)用基類的析構(gòu)函數(shù)清理基類成員。因?yàn)檫@樣才能保證派生類對(duì)象先清理派生類成員再清理基類成員的順序。
  5. 派生類對(duì)象初始化先調(diào)用基類構(gòu)造再調(diào)派生類構(gòu)造。
  6. 派生類對(duì)象析構(gòu)清理先調(diào)用派生類析構(gòu)再調(diào)基類的析構(gòu)。
class person
{
public:
	person(const char* name = "pxl")
		:_name(name)
	{}
	person(const person& p)
		:_name(p._name)
	{}
	person& operator=(const person& p)
	{
		if (this != *p){
			_name = p._name;
		}
		return *this;
	}
	~person()
	{}
protected:
	string _name;
};
class student :public person
{
public:
	student(const char* name, int num)
		:person(name)//顯示調(diào)用基類的構(gòu)造函數(shù)初始化基類成員
		, _num(num)
	{}
	student(const student& s)
		:person(s)//注意這里有個(gè)隱式的切片操作 person& p = s;
		, _num(s._num)
	{}
	student& operator=(const student& s)
	{
		if (this != &s){
			person::operator=(s);//調(diào)用基類的operator=完成基類的賦值
			_num = s._num;
		}
		return *this;
	}
	~student()
	{
		cout << "~student()" << endl;
		//注意這里會(huì)自動(dòng)調(diào)用父類析構(gòu)
	}
protected:
	int _num;
};
void test()
{
	student s1("ppp", 20);
	student s2(s1);
	student s3("xxx", 30);
	s1 = s3;
}

??留意代碼中注釋部分!

五、復(fù)雜菱形繼承及菱形虛擬繼承

5.1、繼承分類

單繼承:一個(gè)子類只有一個(gè)直接父類時(shí)稱為單繼承

多繼承:一個(gè)子類有兩個(gè)或者兩個(gè)以上直接父類時(shí)稱這個(gè)繼承關(guān)系為多繼承

菱形繼承:兩個(gè)派生類繼承同一個(gè)基類,又有某個(gè)類同時(shí)繼承者兩個(gè)派生類。菱形繼承帶來(lái)的主要問(wèn)題是子類繼承兩份相同的數(shù)據(jù),導(dǎo)致資源浪費(fèi)以及毫無(wú)意義。

利用虛繼承可以解決菱形繼承問(wèn)題

????????????????????????????????????????????????????????

對(duì)于菱形繼承的二義性問(wèn)題,我們可以在訪問(wèn)的時(shí)候加上類域,這樣是可以解決的,但是數(shù)據(jù)冗余無(wú)法解決。所以下面引入虛擬繼承!

5.2、虛擬繼承解決菱形繼承問(wèn)題原理

為了研究虛擬繼承原理,我們給出一個(gè)簡(jiǎn)單的菱形繼承體系,再借助內(nèi)存窗口觀察對(duì)象成員模型。

class A{
public:
	int _a;
};
class B:public A
{
public:
	int _b;
};
class C :public A
{
public:
	int _c;
};
class D :public B, public C
{
public:
	int _d;
};
int main()
{
	D d;
	d.B::_a = 1;
	d.C::_a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;
	system("pause");
	return 0;
}

如圖是菱形繼承的內(nèi)存對(duì)象成員模型,可以看出來(lái)數(shù)據(jù)冗余!??!

下面是菱形虛擬繼承的內(nèi)存對(duì)象成員模型:

這里可以分析出D對(duì)象將A放在了對(duì)象組成的最下面,這個(gè)A同時(shí)屬于B和C,那么B和C如何去找到公共的A呢?

這里通過(guò)B和C的兩個(gè)指針,指向一張表。這兩個(gè)指針叫虛基表指針,這兩個(gè)表叫虛基表,虛基表中存的是偏移量。通過(guò)偏移量可以找到下面的A。

到此這篇關(guān)于C++繼承的賦值轉(zhuǎn)換與菱形虛擬繼承深入詳解的文章就介紹到這了,更多相關(guān)C++繼承的賦值轉(zhuǎn)換內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一篇文章帶你了解C語(yǔ)言的選擇結(jié)構(gòu)

    一篇文章帶你了解C語(yǔ)言的選擇結(jié)構(gòu)

    這篇文章主要為大家介紹了C語(yǔ)言的選擇結(jié)構(gòu),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-01-01
  • C++中的std::nothrow使用

    C++中的std::nothrow使用

    這篇文章主要介紹了C++中的std::nothrow使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • Unity3D實(shí)現(xiàn)經(jīng)典小游戲Pacman

    Unity3D實(shí)現(xiàn)經(jīng)典小游戲Pacman

    這篇文章主要介紹了基于Unity3D制作一做個(gè)經(jīng)典小游戲Pacman,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Unity3D有一定的幫助,感興趣的小伙伴可以了解一下
    2021-12-12
  • C++時(shí)間函數(shù)整理詳解

    C++時(shí)間函數(shù)整理詳解

    C++中并沒(méi)有針對(duì)時(shí)間特意提供特定的時(shí)間類型,而是直接繼承了C語(yǔ)言的結(jié)構(gòu)以及函數(shù),因此在C++中使用時(shí)間函數(shù)需要引用<ctime>頭文件,這篇文章主要介紹了C++時(shí)間函數(shù)
    2022-10-10
  • C++ 如何用cout輸出hex,oct,dec的解決方法

    C++ 如何用cout輸出hex,oct,dec的解決方法

    本篇文章是對(duì)C++中如何用cout輸出hex,oct,dec的方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • C語(yǔ)言用棧模擬實(shí)現(xiàn)隊(duì)列問(wèn)題詳解

    C語(yǔ)言用棧模擬實(shí)現(xiàn)隊(duì)列問(wèn)題詳解

    本片文章帶你分析如何用兩個(gè)棧,并且只使用棧的基本功能來(lái)模擬實(shí)現(xiàn)隊(duì)列,其中同樣只實(shí)現(xiàn)隊(duì)列的基本功能,感興趣的朋友來(lái)看看吧
    2022-04-04
  • 你真的知道C++對(duì)象大小嗎?

    你真的知道C++對(duì)象大小嗎?

    這篇文章主要給大家介紹了關(guān)于C++對(duì)象大小的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • 在c和c++中實(shí)現(xiàn)函數(shù)回調(diào)

    在c和c++中實(shí)現(xiàn)函數(shù)回調(diào)

    如何在c和c++中實(shí)現(xiàn)函數(shù)回調(diào)呢?現(xiàn)在小編就和大家分享一下在c/c++中實(shí)現(xiàn)函數(shù)回調(diào)的示例代碼,需要的朋友可以參考下
    2013-07-07
  • C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的掃雷游戲

    C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的掃雷游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單的掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • C語(yǔ)言通過(guò)三種方法實(shí)現(xiàn)屬于你的通訊錄

    C語(yǔ)言通過(guò)三種方法實(shí)現(xiàn)屬于你的通訊錄

    本文將實(shí)現(xiàn)一個(gè)通訊錄,來(lái)實(shí)現(xiàn)人員的增刪插改功能。文中通過(guò)三種形式來(lái)實(shí)現(xiàn)用戶的增刪插改,其實(shí)也就是一點(diǎn)點(diǎn)的優(yōu)化版本,從靜態(tài)的實(shí)現(xiàn),到動(dòng)態(tài)的實(shí)現(xiàn),最后以文件的形式來(lái)完成,請(qǐng)大家和我一起往下看吧
    2022-11-11

最新評(píng)論