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

C++繼承的賦值轉換與菱形虛擬繼承深入詳解

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

一、繼承的概念及定義

繼承是面向對象三大特性之一。

1.1、繼承的概念

繼承(inheritance)機制是面向對象程序設計使代碼可以復用的最重要的手段,它允許程序員在保持原有類特性的基礎上進行擴展,增加功能,這樣產生新的類,稱派生類。繼承呈現(xiàn)了面向對象程序設計的層次結構,體現(xiàn)了由簡單到復雜的認知過程。以前我們接觸的復用都是函數(shù)復用。繼承是類設計層次的復用。

1.2、繼承的定義

繼承的語法:class 子類 : 繼承方式 父類

繼承方式:

  • 共有繼承
  • 私有繼承
  • 保護繼承

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

二、基類和派生類對象賦值轉換

  • 派生類對象可以賦值給基類的對象/基類的指針/基類的引用。這里有個形象的說法叫切片或者切割。寓意把派生類中父類那部分切來賦值過去。
  • 基類對象不能賦值給派生類對象。
  • 基類的指針或者引用可以通過強制類型轉換賦值給派生類的指針或者引用。但是必須是基類的指針是指向派生類對象時才是安全的。

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

三、繼承中的作用域

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

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

class Base {
public:
	Base(){
		m_A = 100;
	}
	void func(){
		cout << "Base - func()調用" << endl;
	}
	void func(int a){
		cout << "Base - func(int a)調用" << endl;
	}
public:
	int m_A;
};
class Son : public Base {
public:
	Son(){
		m_A = 200;
	}
	//當子類與父類擁有同名的成員函數(shù),子類會隱藏父類中所有版本的同名成員函數(shù)
	//如果想訪問父類中被隱藏的同名成員函數(shù),需要加父類的作用域
	void func()
	{
		cout << "Son - func()調用" << 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);
}

??????總結:

  • 子類對象可以直接訪問到子類中同名成員
  • 子類對象加作用域可以訪問到父類同名成員
  • 當子類與父類擁有同名的成員函數(shù),子類會隱藏父類中同名成員函數(shù),加作用域可以訪問到父類中同名函數(shù)。
  • 當然父類對象隨便調用父類成員。

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

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

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

靜態(tài)成員和非靜態(tài)成員出現(xià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()
{
	//通過對象訪問
	cout << "通過對象訪問: " << endl;
	Son s;
	cout << "Son  下 m_A = " << s.m_A << endl;
	cout << "Base 下 m_A = " << s.Base::m_A << endl;
	//通過類名訪問
	cout << "通過類名訪問: " << endl;
	cout << "Son  下 m_A = " << Son::m_A << endl;
	cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}
//同名成員函數(shù)
void test02()
{
	//通過對象訪問
	cout << "通過對象訪問: " << endl;
	Son s;
	s.func();
	s.Base::func();
	cout << "通過類名訪問: " << endl;
	Son::func();
	Son::Base::func();
	//出現(xiàn)同名,子類會隱藏掉父類中所有同名成員函數(shù),需要加作作用域訪問
	Son::Base::func(100);
}

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

3.3、繼承與友元

友元關系不可以繼承,也就是說基類的友元不要可以訪問子類的私有成員和保護成員。

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

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

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

class person
{
public:
	person()
	{
		_count++;
	}
protected:
	string _name;
public:
	static int _count;//統(tǒng)計人數(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
請按任意鍵繼續(xù). .

代碼解釋:因為子類對象構造是會調用基類的構造函數(shù),所以每實例化一個子類對象都會調用一次基類構造,從而_count++,并且靜態(tài)成員是整個類共享的,所以無論哪個子類都可修改?。?!

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

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

  1. 派生類的構造函數(shù)必須調用基類的構造函數(shù)初始化基類的那一部分成員。如果基類沒有默認的構造函數(shù),則必須在派生類構造函數(shù)的初始化列表階段顯示調用。
  2. 派生類的拷貝構造函數(shù)必須調用基類的拷貝構造完成基類的拷貝初始化。
  3. 派生類的operator=必須要調用基類的operator=完成基類的復制。
  4. 派生類的析構函數(shù)會在被調用完成后自動調用基類的析構函數(shù)清理基類成員。因為這樣才能保證派生類對象先清理派生類成員再清理基類成員的順序。
  5. 派生類對象初始化先調用基類構造再調派生類構造。
  6. 派生類對象析構清理先調用派生類析構再調基類的析構。
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)//顯示調用基類的構造函數(shù)初始化基類成員
		, _num(num)
	{}
	student(const student& s)
		:person(s)//注意這里有個隱式的切片操作 person& p = s;
		, _num(s._num)
	{}
	student& operator=(const student& s)
	{
		if (this != &s){
			person::operator=(s);//調用基類的operator=完成基類的賦值
			_num = s._num;
		}
		return *this;
	}
	~student()
	{
		cout << "~student()" << endl;
		//注意這里會自動調用父類析構
	}
protected:
	int _num;
};
void test()
{
	student s1("ppp", 20);
	student s2(s1);
	student s3("xxx", 30);
	s1 = s3;
}

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

五、復雜菱形繼承及菱形虛擬繼承

5.1、繼承分類

單繼承:一個子類只有一個直接父類時稱為單繼承

多繼承:一個子類有兩個或者兩個以上直接父類時稱這個繼承關系為多繼承

菱形繼承:兩個派生類繼承同一個基類,又有某個類同時繼承者兩個派生類。菱形繼承帶來的主要問題是子類繼承兩份相同的數(shù)據(jù),導致資源浪費以及毫無意義。

利用虛繼承可以解決菱形繼承問題

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

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

5.2、虛擬繼承解決菱形繼承問題原理

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

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;
}

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

下面是菱形虛擬繼承的內存對象成員模型:

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

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

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

相關文章

  • 一篇文章帶你了解C語言的選擇結構

    一篇文章帶你了解C語言的選擇結構

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

    C++中的std::nothrow使用

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

    Unity3D實現(xiàn)經典小游戲Pacman

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

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

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

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

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

    C語言用棧模擬實現(xiàn)隊列問題詳解

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

    你真的知道C++對象大小嗎?

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

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

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

    C語言實現(xiàn)簡單的掃雷游戲

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

    C語言通過三種方法實現(xiàn)屬于你的通訊錄

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

最新評論