C++繼承一覽

繼承的概念及定義

  繼承機制是面向對象程序設計是代碼可以複用的重要手段,它允許程序員在保持原有類特性的基礎上進行擴展,增加功能,這樣產生的類稱爲派生類。繼承呈現了面向對象程序設計的層次結構,體現了由簡單到複雜的認知過程。繼承是類設計層次的複用,可以解決程序中代碼的冗餘,繼承後基類的成員都會變成派生類的一部分。

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	void Print()
	{
		cout << "name" << _name << endl;
		cout << "age" << _age << endl;
	}
private:
	string _name = "Bob";
	int _age = 30;
};

class Student : public Person
{
private:
	int _id = 100;
};

int main()
{
	Student s;
	s.Print();

	return 0;
}
繼承定義格式爲:

在這裏插入圖片描述

不同繼承方式

  1、基類private成員在派生類中無論以什麼方式繼承都是不可見的。這裏的不可見是指基類的私有成員還是被繼承到了派生類對象中,但是語法上限制派生類對象不管在類裏面還是類外面都不能去訪問它。
  2、 基類private成員在派生類中是不能被訪問,如果基類成員不想在類外直接被訪問,但需要在派生類中能訪問,就定義爲protected。可以看出保護成員限定符是因繼承纔出現的。繼承的時候最好將成員變量設置成protected,這樣即可以在子類中使用,又可以保證父類的封裝
  3、基類的私有成員在子類都是不可見。基類的其他成員在子類的訪問方式爲 Min(成員在基類的訪問限定符,繼承方式),public > protected > private。
  4、使用關鍵字class時默認的繼承方式是private,使用struct時默認的繼承方式是public,不過最好顯示的寫出繼承方式。
  5、在實際運用中一般使用都是public繼承,幾乎很少使用protetced/private繼承,也不提倡使用protetced/private繼承,因爲protetced/private繼承下來的成員都只能在派生類的類裏面使用,實際中擴展維護性不強。

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

  派生類對象可以賦值給基類的對象 / 基類的指針 / 基類的引用。將派生類對象幅值給基類是切片操作,就是將子類中的父類的那一部分切出來幅值給父類對象,不是隱式類型轉換,反過來不行,因爲基類中有一部分東西是不能滿足派生類中的值。雖然父類指針可以通過強制類型轉換賦給子類指針,但是必須是父類指針指向的是子類對象,否則是不安全的,會訪問越界。

繼承中的作用域

  在繼承體系中基類和派生類都有獨立的作用域。子類和父類中有同名成員,子類成員將屏蔽父類堆同名成員的直接訪問,這種情況叫隱藏,也叫重定義。如果想使用這些被隱藏的成員,需要加父類的作用域。

派生類的默認成員函數

  1、 派生類的構造函數必須調用基類的構造函數初始化基類的那一部分成員。如果基類沒有默認的構造函數,則必須在派生類構造函數的初始化列表階段顯式調用。
  2、 派生類的拷貝構造函數必須調用基類的拷貝構造完成基類的拷貝初始化。
  3、 如果派生類的賦值運算符重載函數是系統自動生成的,就會主動去調用基類的賦值運算符重載函數,如果在派生類中已經顯式的給出了賦值運算符重載函數,並且沒有顯式的調用基類的賦值運算符重載函數,編譯器不會主動去調用。
  4、 派生類的析構函數在被調用完成後纔會去自動調用基類的析構函數清理基類成員。因爲這樣才能保證派生類對象先清理派生類成員再清理基類成員的順序。
  5、 派生類對象初始化先調用基類構造再調派生類構造。
  6、 派生類對象析構清理先調用派生類析構再調基類的析構。

繼承與友元

  友元關係不能繼承,也就是說基類的友元函數只能訪問從基類中繼承的成員,不能訪問派生類中的私有和保護成員。

繼承與靜態成員

  靜態成員爲所有繼承體系中的所有成員共享,保持全局唯一性。無論派生出多少個派生類,都只有一個靜態成員實例。

複雜的菱形繼承及菱形虛擬繼承

  單繼承是一個派生類只有一個直接基類,多繼承是一個派生類有有個或以上的直接基類,菱形繼承是多繼承中的一種特殊情況
代碼實現

#include <iostream>
#include <string>

using namespace std;

class Person
{
public:
	string _name;
};

class Student : public Person
{
protected:
	int _sid = 100;
};

class Teacher : public Person
{
protected:
	int _tid = 99;
};

class Classroom : public Teacher , public Student
{
protected:
	int _number = 2;
};

在這裏插入圖片描述
  菱形繼承有一個最大的問題就是數據的二義性和冗餘,雖然可以顯示指定訪問哪個父類的成員可以解決二義性問題,但是數據冗餘問題無法解決
在這裏插入圖片描述
  通過虛擬繼承可以解決菱形繼承的二義性和數據冗餘的問題(繼承同一個父類的兩個類設置爲虛擬繼承)。如上面的繼承關係,在Student和Teacher的繼承Person時使用虛擬繼承,即可解決問題。需要注意的是,虛擬繼承不能在其他地方去使用。
  菱形虛擬繼承的底層是通過虛基表實現共有的數據只存放一份,虛基表中存放的是偏移量。通過虛基表指針訪問虛基表拿到偏移量,然後再通過偏移量拿到公有的數據。
在這裏插入圖片描述

繼承和組合

  1、public繼承是一種is-a的關係,也就是說每個派生類對象是一個基類對象。
  2、組合是一種has-a的關係,假設B組合了A,每個B對象中都有一個A對象。
  3、繼承允許用戶根據基類類的實現來定義派生類的實現。這種通過派生類的複用同常被稱爲白箱複用。繼承一定程度上破化哦了基類的封裝,基類的改變,對派生類有很大的影響。繼承類之間依賴關係很強,耦合度高。
  4、組合是繼承之外的另一種複用選擇。新的更復雜的功能可以通過組裝和組合對象來獲得。組合要求被組合的對象有良好的定義接口,這種複用被稱爲黑箱複用。組合類之間沒有很強的依賴關係,耦合度低。是由組合有助於保持每個類被封裝。
  5、在實際中儘量使用組合,組合的耦合度低,代碼的維護性好。但是繼承也有用武之地,比如說要實現多態就必須要繼承。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章