C++基础(17)——继承
目錄
1.繼承的概述
1.1概念
1.2繼承的基本用法
?1.3繼承的好處
1.4語法
2.繼承方式
2.1繼承語法
2.2繼承方式
3.繼承中的構造與析構順序
4.繼承同名成員處理方式
4.1繼承同名成員屬性處理方式
?4.2繼承同名成員函數處理方式
?5.同名靜態成員處理
?6.多繼承語法
7.菱形繼承案例
7.1菱形繼承概念
1.繼承的概述
1.1概念
繼承是面向對象三大特性之一。我們發現在定義類的時候,下級別的成員除了擁有上一級別的的共性,還有自己的特性,這時候我們可以考慮使用繼承的技術來減少重復的代碼。
1.2繼承的基本用法
例如我們看到很多的網站中,都有公共的頭部、公共的底部、甚至公共的左側列表,只有中心內容不同,接下來我們分別利用普通寫法和繼承的寫法來實現網頁中的內容,看一下繼承存在的意義以及好處。
#include<iostream> using namespace std;class java {//java網站類 public:void header() {cout << "首頁、公開課、登錄、注冊……(公共頭部)" << endl;}void foter() {cout << "幫助中心、交流合作、站內地圖……(公共底部)" << endl;}void left(){cout << "Java、python、C++……(公共分類列表)" << endl;}void content() {cout << "java學科視頻" << endl;} };//Python頁面 class Python {//java網站類 public:void header() {cout << "首頁、公開課、登錄、注冊……(公共頭部)" << endl;}void foter() {cout << "幫助中心、交流合作、站內地圖……(公共底部)" << endl;}void left() {cout << "Java、python、C++……(公共分類列表)" << endl;}void content() {cout << "Python學科視頻" << endl;} };//C++學習頁面 class Cpp{//java網站類 public:void header() {cout << "首頁、公開課、登錄、注冊……(公共頭部)" << endl;}void foter() {cout << "幫助中心、交流合作、站內地圖……(公共底部)" << endl;}void left() {cout << "Java、python、C++……(公共分類列表)" << endl;}void content() {cout << "C++學科視頻" << endl;} };void test02() {java ja;cout << "java下載視頻頁面如下" << endl;ja.header();ja.foter();ja.left();ja.content();cout << "-----------------------" << endl;cout << "Python下載視頻頁面如下" << endl;Python py;py.header();py.foter();py.left();py.content();cout << "-----------------------" << endl;cout << "C++下載視頻頁面如下" << endl;Cpp cp;cp.header();cp.foter();cp.left();cp.content(); }void main() {test02(); }可以看到,代碼沒有問題,但是有太多的冗余代碼,這樣看起來太low。所以我們可以考慮使用繼承的方式:
class BasePage {//公共的界面 public:void header() {cout << "首頁、公開課、登錄、注冊……(公共頭部)" << endl;}void foter() {cout << "幫助中心、交流合作、站內地圖……(公共底部)" << endl;}void left() {cout << "Java、python、C++……(公共分類列表)" << endl;} };//Java頁面 class Java :public BasePage {//繼承公共頁面 public:void content() {cout << "Java學科視頻" << endl;} };//Python頁面 class Python :public BasePage {//繼承公共頁面 public:void content() {cout << "Python學科視頻" << endl;} };//C++頁面 class Cpp :public BasePage {//繼承公共頁面 public:void content() {cout << "C++學科視頻" << endl;} };void test02() {Java ja;cout << "java下載視頻頁面如下" << endl;ja.header();ja.foter();ja.left();ja.content();cout << "-----------------------" << endl;cout << "Python下載視頻頁面如下" << endl;Python py;py.header();py.foter();py.left();py.content();cout << "-----------------------" << endl;cout << "C++下載視頻頁面如下" << endl;Cpp cp;cp.header();cp.foter();cp.left();cp.content(); }void main() {test02(); }?
?1.3繼承的好處
減少重復冗余的代碼
1.4語法
class 子類:繼承方式? 父類 ,如下所示:
?子類:也成為派生類,父類也稱為基類
2.繼承方式
2.1繼承語法
class 子類:繼承方式? 父類?
2.2繼承方式
- 公共繼承
- 保護繼承
- 私有繼承
解析:
- 子類不能繼承父類的私有屬性
- 子類如果是公共繼承,在父類中是公共的屬性在子類中同樣是,在父類中是保護繼承的屬性,在子類中也是如此
- 如果子類是保護繼承,父類中的公共屬性,在子類中變為保護權限,父類保護,子類也是保護
- 如果子類是私有繼承父類中的公共權限和保護權限屬性,在子類中都變為私有權限
?
我們也可以利用開發人員命令提示工具查看對象模型(VS自帶的Developer Command Prompt)
- 查看需要運行的程序在哪個盤,如果在D盤就輸入D:就跳轉到D盤
- 跳轉文件的路徑:cd? 具體的路徑
- 查看對象模型輸入:cl /d1 reportSingleClassLayout類名 “文件名”?
如下所示:
3.繼承中的構造與析構順序
子類繼承父類后,當創建子類對象,也會調用父類的構造函數
問題:父類和子類的構造和析構順序誰先誰后呢?
#include<iostream> using namespace std;//繼承中的構造與析構的順序 class Base1 { public:Base1() {cout << "Base1的構造函數" << endl;}~Base1() {cout << "Base1的析構函數" << endl;} };class Son :public Base1 { public:Son() {cout << "Son的構造函數" << endl;}~Son() {cout << "Son的析構函數" << endl;} };void test01() {//Base1 b;Son s;}void main() {test01(); }可以看出,繼承中的構造與析構順序如下:
先構造父類,再構造子類,析構的順序與構造的順序相反
4.繼承同名成員處理方式
問題:當子類與父類出現同名的成員,如何通過子類對象,訪問到子類或者父類中的同名的數據?
- 訪問子類同名成員? 直接訪問即可
- 訪問父類的同名成員需要加作用域
4.1繼承同名成員屬性處理方式
#include<iostream> using namespace std;//繼承中的構造與析構的順序 class Base1 { public:Base1() {m_A = 100;}int m_A ; };class Son :public Base1 { public:Son() {m_A = 200;}int m_A; };void test01() {Son s;cout << "Son m_A=" <<s. m_A << endl;}void main() {test01(); }可以看出如果直接訪問,m_A是200,說明如果出現同名直接訪問是訪問的自身的成員。
?如果需要拿到父類的成員:
只需要加一個父類的作用域即可訪問到同名中父類的屬性。?
?4.2繼承同名成員函數處理方式
#include<iostream> using namespace std;//繼承中的構造與析構的順序 class Base1 { public:Base1() {m_A = 100;}void func() {cout << "這是base中的函數調用" << endl;}int m_A ; };class Son :public Base1 { public:Son() {m_A = 200;}void func() {cout << "這是son中的函數調用" << endl;}int m_A; };void test01() {Son s;cout << "Son m_A=" <<s. m_A << endl;cout << "Base m_A=" << s.Base1::m_A << endl; }void test02() {Son s;s.func();//當出現重名調用的還是子類的s.Base1::func();//調用父類的成員函數 } void main() {test01();test02(); }?5.同名靜態成員處理
問題:繼承同名的靜態成員在子類對象上如何進行訪問?
靜態成員和非靜態成員出現同名,處理方式一樣。
- 訪問子類同名成員,直接訪問即可
- 訪問父類同名成員,需要加作用域
靜態成員的屬性特點:
- 編譯階段分配內存
- 所有對象共享一份數據
- 類內聲明,類外要初始化
可以看到直接訪問是訪問的子類的,要想訪問父類需要加一個作用域
?
?訪問可以通過對象訪問,也可以通過類名訪問,如下:
?
#include<iostream> using namespace std;class Base1 { public:static int m_A ;//類內聲明 }; int Base1::m_A = 100;//類外初始化class Son :public Base1 { public:static int m_A; }; int Son::m_A = 200;void test01() {//通過對象訪問Son s;cout << "Son m_A=" << s.m_A << endl;cout << "Base m_A=" << s.Base1::m_A << endl;//通過類名訪問cout << "通過類名訪問:" << endl;cout << "Son 下 m_A=" << Son::m_A<<endl;cout << "Base 下 m_A=" << Son::Base1::m_A << endl; } void main() {test01(); }繼承靜態的成員屬性以及成員函數的完整代碼如下:
#include<iostream> using namespace std;class Base1 { public:static void func() {cout << "Base static void func()" << endl;}static int m_A ;//類內聲明 }; int Base1::m_A = 100;//類外初始化class Son :public Base1 { public:static void func() {cout << "Son static void func()" << endl;}static int m_A; }; int Son::m_A = 200;void test01() {//通過對象訪問Son s;cout << "Son m_A=" << s.m_A << endl;cout << "Base m_A=" << s.Base1::m_A << endl;//通過類名訪問cout << "通過類名訪問:" << endl;cout << "Son 下 m_A=" << Son::m_A<<endl;cout << "Base 下 m_A=" << Son::Base1::m_A << endl; }void test02() {//通過對象訪問cout << "通過對象訪問" << endl;Son s;s.func();s.Base1::func();//通過類訪問cout << "通過類訪問" << endl;Son::func();Son::Base1::func(); } void main() {test01();cout << "***********************" << endl;test02(); }?
?6.多繼承語法
C++允許一個類繼承多個類
語法:class 子類 :繼承方式 父類1,繼承方式 父類2 ……
注意:多繼承可能會引發父類中有同名成員的出現,需要加作用域區分
C++在實際開發中不建議使用多繼承
#include<iostream> using namespace std;class Base1 { public:Base1() {m_A = 100;}int m_A; };class Base2 { public:Base2() {m_B = 200;}int m_B; };//子類繼承Base1和Base2 class Son :public Base1,public Base2{ public:Son() {m_C = 300;m_D = 400;}int m_C;int m_D; };void test01() {Son s;cout << "sizeof Son=" << sizeof(s) << endl; }void main() {test01();}我們可以看看類的結構,具體如何打開的見上一個博客。?
我們可以看出size是16,Son這個類,在Base1中繼承了m_A在Base2中繼承了m_B,自己還有m_C和m_D。
7.菱形繼承案例
7.1菱形繼承概念
- 兩個派生類繼承同一個基類
- 又有某個類同時繼承兩個派生類
- 這種繼承稱為菱形繼承,或者鉆石繼承
例如:羊和駱駝繼承了動物這個類,草泥馬這個動物又繼承了羊和駱駝這個類,若動作有一個m_A這個屬性,那么羊和駱駝也繼承了動物類中的m_A,那么草泥馬這個類就有兩份m_A,而我們只需要一份即可,那怎么辦?
#include<iostream> using namespace std;//動物類 class Animal {int m_Age; };//羊類 class Sheep:public Animal {};//駱駝類 class Camel:public Animal {};//羊駝類(草泥馬類) class SheepCamel :public Sheep, public Camel {};void test01() {SheepCamel sc;sc.m_Age = 10; }void main() {test01();}test01中直接對m_Age=10賦值會出現“SheepCamel m_Age不明確”的錯誤。所以:當菱形繼承,兩個父類擁有相同的數據,需要加以作用域區分
那么草泥馬(羊駝)的m_Age到底是多少呢,我們只需要一份數據就可以了。我們打出報告可以看到:SheepCamel繼承了兩個類,一個是Sheep一個是Camel,有兩個m_Age,但是我們只需要一個m_Age那么該如何解決呢?
?利用虛繼承可以解決菱形繼承的問題。即為,在繼承之前加一個關鍵字virtual變為虛繼承。
?
#include<iostream> using namespace std;//動物類 class Animal { public:int m_Age; };//利用虛繼承解決菱形繼承問題,繼承之前加上關鍵詞virtual變為虛繼承 //Animal類稱為虛基類 //羊類 class Sheep:virtual public Animal {};//駱駝類 class Camel:virtual public Animal {};//羊駝類(草泥馬類) class SheepCamel :public Sheep, public Camel {};void test01() {SheepCamel sc;sc.Sheep::m_Age = 10;sc.Camel::m_Age = 28;//當菱形繼承,兩個父類擁有相同的數據,需要加以作用域區分cout << "sc.Sheep::m_Age = " << sc.Sheep::m_Age << endl;cout << "sc.Camel::m_Age=" << sc.Camel::m_Age << endl;cout << "SheepCamel m_Age=" << sc.m_Age << endl; }void main() {test01();}?經過修改后發現年齡都變成28了。我們再看看報告:
可以看到與沒有加關鍵詞virtual時的結構完全不一樣了。可以看到SheepCamel的m_Age只有一份了,而從Sheep和Camel繼承下來的是vbptr。vbptr(virtual base pointer)表示虛基類指針?
總結
以上是生活随笔為你收集整理的C++基础(17)——继承的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql建库1044_Mysql创建数
- 下一篇: Vue 项目中各种痛点问题及方案(建议收