日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++从0到1的入门级教学(十三)——继承

發布時間:2023/12/9 c/c++ 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++从0到1的入门级教学(十三)——继承 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 13 繼承
    • 13.1 繼承的基本語法
    • 13.2 繼承方式
    • 13.3 繼承的對象模型
    • 13.4 繼承中構造和析構順序
    • 13.5 繼承同名成員處理方式
    • 13.6 繼承同名靜態成員處理方式
    • 13.7 多繼承語法
    • 13.8 菱形繼承

13 繼承

繼承是面向對象三大特性之一。有些類和類之間存在特殊的關系,如下圖所示:

我們發現,定義這些類的時候,每個子節點都有父節點的特性,且有自己的特性。如果在定義類的時候,有諸如以上的關系,那么我們在C++中和Java中都可以采用繼承的方式來簡化代碼。

13.1 繼承的基本語法

繼承的語法是class 子類類名:繼承方式 父類類名。讓我們看一下下面的例子:

#include <iostream> using namespace std;//class Java //{ //public: // void header() // { // cout << "首頁、公開課、登錄、注冊...公共頭部" << endl; // } // void footer() // { // cout << "幫助中心、交流合作、站內地圖...(公共底部)" << endl; // } // void left() // { // cout << "Java、Python、C++、...(公共分類列表)" << endl; // } // void content() // { // cout << "Java學科視頻" << endl; // } //}; // //class Python //{ //public: // void header() // { // cout << "首頁、公開課、登錄、注冊...公共頭部" << endl; // } // void footer() // { // cout << "幫助中心、交流合作、站內地圖...(公共底部)" << endl; // } // void left() // { // cout << "Java、Python、C++、...(公共分類列表)" << endl; // } // void content() // { // cout << "Python學科視頻" << endl; // } //}; // //class CPP //{ //public: // void header() // { // cout << "首頁、公開課、登錄、注冊...公共頭部" << endl; // } // void footer() // { // cout << "幫助中心、交流合作、站內地圖...(公共底部)" << endl; // } // void left() // { // cout << "Java、Python、C++、...(公共分類列表)" << endl; // } // void content() // { // cout << "C++學科視頻" << endl; // } //};//繼承公共頁面 class BasePage { public:void header(){cout << "首頁、公開課、登錄、注冊...公共頭部" << endl;}void footer(){cout << "幫助中心、交流合作、站內地圖...(公共底部)" << endl;}void left(){cout << "Java、Python、C++、...(公共分類列表)" << endl;} };//Java頁面 class Java :public BasePage { public :void content() {cout << "Java學科視頻" << endl;} };//Java頁面 class Python :public BasePage { public:void content(){cout << "Python學科視頻" << endl;} };//C++頁面 class CPP :public BasePage { public:void content(){cout << "C++學科視頻" << endl;} };void test01() {cout << "Java 下載視頻頁面如下:" << endl;Java java;java.header();java.footer();java.left();java.content();cout << "Python 下載視頻頁面如下:" << endl;Python python;python.header();python.footer();python.left();python.content();cout << "C++ 下載視頻頁面如下:" << endl;CPP cpp;cpp.header();cpp.footer();cpp.left();cpp.content(); }int main() {test01(); }

子類有時候也叫派生類,而父類有時候也叫基類。

繼承的使用如果是對于學習過Java的人來說是非常簡單的,但不同于Java的是,其并不是像Java一樣是單繼承而是多繼承,這個觀點我們在后面持續提到,不用著急。

13.2 繼承方式

我們已經知道繼承的語法怎么使用了,但是我們對繼承的方式還是了解頗淺,在這一小節中,讓我們看下繼承有哪些方式吧。

繼承的方式一共有三類:

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

讓我們看一下繼承方式的示意圖:

對于父類私有的東西,子類無論如何繼承都無法得到,這是其一;除了私有權限的東西,其他的都好說,如果是公共繼承,那么子類會將父類的公有屬性和保護屬性繼承,并且不變其權限;如果是保護繼承,那么在繼承父類除私有屬性外,其他屬性一律變為子類保護屬性;同樣地,如果是私有繼承,那么在繼承父類除私有屬性外,其他屬性一律變為子類私有屬性。

#include <iostream> using namespace std;//繼承方式 //公共繼承 class Base1 { public:int m_A; protected:int m_B; private:int m_C; };class Son1 :public Base1 { public:void func() {m_A = 10;//父類中的公共權限成員,到子類中依然是公共權限m_B = 10;//父類中的保護權限成員,到了子類中依然是保護權限//m_C = 10;不可訪問,父類中的私有權限成員,子類繼承不了} };void test01() {Son1 s1;s1.m_A;//公共權限類內可訪問,類外也可訪問//s1.m_B;保護權限類內可訪問,類外不可訪問 }//保護繼承 class Base2 { public:int m_A; protected:int m_B; private:int m_C; };class son2 :protected Base2 { public:void func() {m_A = 100;//父類中公共成員,到了子類中變成保護權限m_B = 100;//父類中保護成員,到了子類中變成了保護權限//m_C = 100;即使是保護繼承,子類依然得不到父類私有成員} };void test02() {son2 s2;//s2.m_A = 100;//在son2中m_A變為保護權限,因此類外訪問不到//s2.m_B = 100;//與上面同理,不多解釋 }//私有繼承 class Base3 { public:int m_A; protected:int m_B; private:int m_C; };class son3 : private Base3 { public:void func(){m_A = 100;//父類中公共成員,到了子類中變成私有權限m_B = 100;//父類中保護成員,到了子類中變成了私有權限//m_C = 100;即使是保護繼承,子類依然得不到父類私有成員} };void test03() {son3 s3;//s3.m_A = 100;//在son3中m_A變為私有權限,因此類外訪問不到//s3.m_B = 100;//與上面同理,不多解釋 }int main() {test01();test02();test03(); }

13.3 繼承的對象模型

有時候我們應該提出一個疑問,對于從父類繼承而來的成員,哪些屬于子類對象?

從下面的代碼中,我們相信可以找到自己想要的答案。

#include <iostream> using namespace std;//繼承中的對象模型 class Base { public:int m_A; protected:int m_B; private:int m_C; };class Son : public Base { public:int m_D; };void test01() {cout << "size of Son = " << sizeof(Son) << endl; }int main() {test01(); }

out:

為什么會這樣?

從結果來看,父類中非靜態成員屬性都會被子類繼承下去。即使是私有的成員,雖然編譯器不能讓其訪問,但是還是能夠繼承下來。

我們想要驗證這個猜想。

對此我們可以打開Vistul stdio的開發者命令行工具。

打開完后。我們回來找到我們的代碼文件對應的位置。

復制備用。

我們使用linux命令在開發者命令行工具中實現文件路徑跳轉。

cd C:\ cd C:\UserWorkStation\3_代碼\C++\Test01

然后使用以下命令查看該文件夾下的文件。

dir

用以下的命令——報告單個類分布圖+類名字+文件名字。

cl /d1 reportSingleClassLayoutSon Test01.cpp

從圖上我們可以看到,實際上父類的私有成員也被子類Son繼承下來了,之所以不能用只不過是受編譯器的影響無法訪問罷了 。

13.4 繼承中構造和析構順序

子類繼承父類后,當創建子類對象,也會調用父類的構造函數。那么此時我們引出問題:父類和子類的構造和析構順序是誰先誰后呢?從下面的代碼中,我們或許能夠找到我們的答案。

#include <iostream> using namespace std;class Base { public:Base() {cout << "Base構造函數!" << endl;}~Base() {cout << "Base析構函數!" << endl;} };class Son :public Base { public:Son() {cout << "Son構造函數!" << endl;}~Son() {cout << "Son析構函數!" << endl;} };void test01() {Son s; }int main() {test01(); }

out:

其實這個結果是顯而易見的。因為你沒有父類哪來的子類,而且子類的供給來源于父類,要想銷毀父類必須先把子類給消除。

13.5 繼承同名成員處理方式

我們不禁拋出一個問題,當子類和父類出現同名的成員時,如何通過子類對象,訪問到子類或父類中同名的數據呢?

很簡單,加修飾符即可。對于訪問子類同名的成員,直接訪問即可,但是如果訪問父類同名成員,加上作用域。

跟著下面的代碼敲一下,我相信你能夠理解我說的是什么。

#include <iostream> using namespace std;class Base { public :Base() {m_A = 100;}int m_A;void func() {cout << "Base-func()調用" << endl;} };class Son :public Base { public:Son() {m_A = 200;}int m_A;void func(){cout << "Son-func()調用" << endl;} };//同名成員屬性的處理方式 void test01() {Son s;cout << "Son的m_A = " << s.m_A << endl;//如果通過子類對象 訪問到父類中同名成員,需要加上作用域cout << "Base的m_A = " << s.Base::m_A << endl; }//同名成員函數的處理方式 void test02() {Son s;s.func();s.Base::func(); }int main() {//test01();test02(); }

在上面的代碼中,明顯地,如果想要調用子類的同名函數,直接按平時那么調用就行了;但是如果想要調用父類的同名函數,那么就需要加上父類::成員實現調用。

還有一個需要考慮的問題是,我們前面講過函數重載。函數重載時,相同名字的函數就會變多,此時又會發生什么情況呢?

我們試著對父類的同名函數重載。

class Base { public :Base() {m_A = 100;}int m_A;void func() {cout << "Base-func()調用" << endl;}void func(int a) {a = 100;} };

這時候如果想要試著不加作用域在子類對象中調用父類的重載函數,那么會失敗。

因為在你不加作用域時,系統會默認為你想要調用的是子類對象中的同名函數,而同名函數沒有重載,重載的是父類的;換而言之,當子類的父類出現同名函數時,如果通過子類對象調用,那么父類的所有同名函數都會被屏蔽,而如果想要得到被屏蔽的函數,加上作用域就能解決這一問題。

13.6 繼承同名靜態成員處理方式

在前面的敘述中,我們談論的都是非靜態成員。這這一小節中,我們試著談論一下靜態成員。

我們不禁發問,靜態成員和非靜態成員處理方法一樣嗎?答案是一樣的。

我們試著敲一下下面的代碼,它能告訴我們答案。

#include <iostream> using namespace std;//繼承中的同名靜態成員處理方式 class Base { public:static int m_A;static void func() {cout << "Base_func" <<endl;} }; int Base::m_A = 100;class Son:public Base { public:static int m_A;static void func(){cout << "Son_func" << endl;} }; int Son::m_A = 200;//同名靜態成員屬性 void test01() {//1、通過對象訪問Son s;cout << "Son的m_A = " << s.m_A << endl;cout << "Base的m_A = " << s.Base::m_A << endl;//2、通過類名訪問cout << "Son的m_A = " << Son::m_A << endl;cout << "Base的m_A = " << Son::Base::m_A << endl; }//同名靜態成員函數 void test02() {//1、通過對象訪問Son s;s.func();s.Base::func();//2、通過類名訪問Son::func();Son::Base::func(); }int main() {//test01();test02(); }

13.7 多繼承語法

不同于Java,C++有著多繼承模式。換而言之,C++允許一個子類繼承多個父類。其語法如下:

class 子類:繼承方式 父類1,繼承方式 父類2…

當然,多繼承會伴隨著同名成員的出現,所以需要加上作用域加以區分。

在實際開發中,是不推薦使用多繼承的。

讓我們試著敲一下下面的代碼,仔細體會其中的意境。

#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; };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; }int main() {test01(); }

out:

多繼承滿足單繼承的一切特性。也就是說,單繼承出現的問題,多繼承也會出現,這時候就需要仔細回顧前面幾個小節的知識了。

13.8 菱形繼承

從名字上看很好理解。菱形繼承就是一個父類分兩子類,然后又有個孫子類繼承兩子類。就這么簡單。當你聽到鉆石繼承這個術語時,一般也是指菱形繼承。

菱形繼承也會出現幾個問題。最簡單的問題就是,如果兩個子類有同名成員,那么在孫類的時候調用兩個子類的同名成員就得加上作用域,以防出現二義性。

還有一個問題是,我們繼承動物的數據時,由于兩個子類的繼承,兩個子類分別占有一個數據,當孫類繼承兩子類時,就會繼承兩份動物數據,但是我們很清楚,這份數據我們只需要一份,為此,我們需要解決這個問題。

在下面的代碼中,我們會提到我們是如何解決這個問題的。

#include <iostream> using namespace std;//動物類 class Animal { public:int m_Age; };//羊類 class Sheep:virtual public Animal {};//駝類 class Tuo :virtual public Animal {};//羊駝類 class YangTuo :public Sheep, public Tuo {};void test01() {YangTuo yt;yt.Sheep::m_Age = 18;yt.Tuo::m_Age = 28;//出現菱形繼承時,兩個父類擁有相同數據,需要加上作用域區分cout << "yt.Sheep::m_Age" << yt.Sheep::m_Age << endl;cout << "yt.Tuo::m_Age" << yt.Tuo::m_Age << endl;//問題2:羊駝的年齡到底是多少?菱形繼承導致了數據有兩份,資源浪費//解決:利用虛繼承就能夠解決上述問題。cout << "yt.m_Age" << yt.m_Age << endl; }int main() {test01(); }

out:

虛繼承、虛基類和虛基類指針

如果我們只想在子類中保留一份數據,那么我們可以定義虛基類,使派生類中只保留一份拷貝。

虛基類的定義方式是,在子類繼承父類時,在冒號后繼承方式前間添加virtual關鍵字,這樣,它們共同繼承的父類我們就叫做虛基類。對應到上述的例子,Animal就是虛基類。

我們使用13.3中學習到的開發者工具看一下類的結構。

從圖中可以看出,虛基類的兩個子類內部供有一個虛基類表(vbtable),該表中記載著對于基類數據的使用,當我們用羊駝類繼承兩子類時,羊駝類中擁有兩根虛基類指針,分別指向兩個虛基類表,以便共享同一份數據。

總結

以上是生活随笔為你收集整理的C++从0到1的入门级教学(十三)——继承的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产毛片毛片毛片毛片毛片 | juliaann第一次和老师 | 国产精品99久久久久久宅男 | 视频在线观看视频 | se日韩| 超碰在线97国产 | 四虎成人在线 | 青青草伊人网 | 欧美日韩国产专区 | 老司机在线观看视频 | 亚洲色图一区二区 | 久久国产精品区 | 羞羞涩涩视频 | 天堂在线播放 | av片在线观看免费 | 爆操白虎逼 | 亚洲成人国产精品 | 亚洲精品一区二区三区婷婷月 | 一区二区视频在线观看 | 男女视频网站 | 国产区在线 | 欧美无砖砖区免费 | 免费一级欧美 | 成年人在线观看 | 久久视频国产 | 在线中文字幕网站 | 亚洲一区二区三区四区在线 | 91美女视频 | 国产在线视频一区二区 | 国产精品福利一区 | 国内少妇精品 | 亚洲无码精品在线观看 | 国产精品亚洲lv粉色 | 三年中国片在线高清观看 | 久热这里只有精品在线 | 99re这里只有精品在线观看 | 人人插人人搞 | 欧美性猛交xxxx黑人 | 少妇一级淫片免费放播放 | 草久免费视频 | av尤物在线 | 福利视频在线 | 欧美囗交做爰视频 | 日韩精品视 | 亚洲男人在线天堂 | 天天操导航 | 亚洲一区图片 | 久草视频手机在线观看 | 黄色不卡 | 色丁香婷婷综合久久 | 琪琪免费视频 | 亚洲第一福利视频 | 女儿的朋友在线播放 | 久久午夜夜伦鲁鲁片 | 好看的黄色网址 | www.猫咪av| 久久久久久久国产精品 | 免费在线观看你懂的 | 乱老熟女一区二区三区 | 人妖ts福利视频一二三区 | 超碰成人在线免费观看 | 免费黄色高清视频 | 国产精品天堂 | 国产h视频在线 | 亚洲高清在线观看视频 | 亚洲图片偷拍区 | 成人在线你懂的 | 日本午夜视频在线观看 | 九九热视频在线 | 美女交配 | 国产又粗又猛又爽又黄 | 99爱国产 | av色婷婷 | 欧美激情在线免费 | 国产美女永久免费 | 色老头影视 | 亚洲精品97久久中文字幕无码 | 久草视频在线免费 | 久久永久视频 | 日韩日韩 | 国产女人18毛片水18精品 | 精品国产黄 | 亚洲国产成人精品久久久 | 看全黄大色黄大片 | 国语精品久久 | 国产一区二区精品在线 | 中文字幕一区二区三区免费视频 | 欧美日韩有码 | 欧美老女人性生活视频 | 成人三级晚上看 | 国产精品伦一区二区三区免费看 | 自拍偷拍福利视频 | 中文字幕首页 | 国产欧美高清 | 欧美综合一区二区三区 | 黄色激情av| 国产字幕在线观看 | 久久综合狠狠综合久久综合88 | 欧美h网站 |