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