c++進階———繼承

1.引言

在一些大的項目中,我們可能要重復定義一些類,但是很麻煩,應該怎么辦呢?舉個簡單的例子,我要做一個全校師生統計表,統計學號,教師編號,姓名,年齡,電話,聯系方式等等,這些內容僅有一點點不同,如果我先定義一個教師類,私有成員包括上述與教師相關的內容,在定義一個學生類,私有成員也包含上述與學生相關的內容,這樣會顯得十分麻煩,而且重復定義,浪費時間。我們不如在這里引入一個新的概念:繼承。通過定義一個基類(也叫父類),其私有成員包括二者共有的內容,之后在分別定義學生和老師類,并繼承基類的私有成員,這樣問題便得以解決!

2.繼承?

在C++中,繼承是一種面向對象編程的核心概念,它允許一個類(稱為派生類或子類)繼承另一個類(稱為基類或父類)的屬性(成員變量)和行為(成員函數)。通過繼承,派生類可以重用基類的代碼,同時還可以添加新的功能或修改繼承的功能。

2.1?繼承的基本概念

繼承是一種“是一種”(is-a)關系,表示派生類是基類的一種特殊形式。例如,DogAnimal 的一種,CarVehicle 的一種。通過繼承,派生類可以自動獲得基類的成員變量和成員函數,并且可以根據需要對這些成員進行擴展或修改.

2.2 繼承方式

  • 公有繼承(public

    • 基類的 public 成員在派生類中保持 public

    • 基類的 protected 成員在派生類中保持 protected

    • 基類的 private 成員在派生類中不可訪問。

  • 保護繼承(protected

    • 基類的 publicprotected 成員在派生類中都變為 protected

    • 基類的 private 成員在派生類中不可訪問。

  • 私有繼承(private

    • 基類的 publicprotected 成員在派生類中都變為 private

    • 基類的 private 成員在派生類中不可訪問。

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

2. 基類private成員在派?類中是不能被訪問,如果基類成員不想在類外直接被訪問,但需要在派生類中能訪問,就定義為protected。可以看出保護成員限定符是因繼承才出現的。

3. 實際上?的表格我們進行?下總結會發現,基類的私有成員在派?類都是不可見。基類的其他成員在派生類的訪問方式 == Min(成員在基類的訪問限定符,繼承方式),public > protected >private。

4. 使用關鍵字class時默認的繼承方式是private,使?struct時默認的繼承方式是public,不過最好顯?的寫出繼承方式。

5. 在實際運用中?般使用都是public繼承,幾乎很少使用protetced/private繼承,也不提倡使用protetced/private繼承,因為protetced/private繼承下來的成員都只能在派生類的類里面使用,實際中擴展維護性不強。

2.3?繼承的示例

我先將完整的代碼展示出來,隨后做解釋:

#include<iostream>
using namespace std;
class person 
{
public:// 進入校園/圖書館/實驗室刷二維碼等身份認證void identity(){cout << "void identity()" << _name << endl;}
protected:string _name = "zhagsan";string _address;string _tel;int _age = 18;
};
class student :public person
{
public:// 學習void study(){//...identity();}
protected:int _stuid;//學號
};
class teacher :public person
{
public:// 授課void teaching(){//...}
protected:string title; // 職稱
};
int main()
{student s;teacher t;s.identity();t.identity();return 0;
}

運行結果:
?

?解釋:student類和teacher類都繼承了person類,而且都是公有繼承,因此可以調用person類的? ? ? ? ? ? ? ? 函數。可以試一下,將public改為protected或者private,程序就會報錯,無法調用基類的? ? ? ? ? ? ? ? identity函數。

體驗一下protected繼承?

class person
{
public:void print(){cout << _name << endl;cout << _age << endl;}
protected:string _name = "張三"; // 姓名
private:int _age = 18; // 年齡
};
class student :protected person
{
public:void func(){// 不可見//cout << _age << endl;print();}
protected:int _stunum = 1; // 學號
};
int main()
{student s;s.func();         //可以運行//s.print();        //派生類的protected成員,類外不能訪問//s._name;return 0;
}

3. 基類和派生類間的轉換

? public繼承的派生類對象可以賦值給基類的指針 / 基類的引用。這里有個形象的說法叫切片或者切

割。寓意把派?類中基類那部分切出來,基類指針或引用指向的是派?類中切出來的基類那部分。

? 基類對象不能賦值給派生類對象。

class Person
{
protected:string _name = "張三"; // 姓int _id = 1;
};
class student :public Person
{
public:void func(){cout << _id << endl;}
protected:int _stunum = 1; // 學號int _id = 2;
};
int main()
{student s;Person* ptr = &s;Person& ref = s;return 0;
}

4. 繼承中的作用域

4.1 隱藏規則:

1. 在繼承體系中基類和派生類都有獨立的作用域。

2. 派?類和基類中有同名成員,派?類成員將屏蔽基類對同名成員的直接訪問,這種情況叫隱藏。

(在派生類成員函數中,可以使用基類::基類成員顯示訪問)

3. 需要注意的是如果是成員函數的隱藏,只需要函數名相同就構成隱藏

4. 注意在實際中在繼承體系??最好不要定義同名的成員

class Person
{
protected:string _name = "張三"; // 姓名int _id = 1;
};
class Student : public Person
{
public:void func(){cout << Person::_id << endl;     //默認的是打印派生類的(自己的)若想打印基類的,要加 // 上“前綴”cout << _id << endl;}
protected:int _stunum = 1; // 學號int _id = 2;
};int main()
{Student s;s.func();return 0;
}

若兩個類的函數是相同的, 則應該這樣訪問

class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){cout << "func(int i)" << i << endl;}
};int main()
{B b;b.fun(10);b.A::fun();return 0;
};

?結果如下:

5. 派生類的默認成員函數

5.1? 4個常見默認成員函數

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

1. 派?類的構造函數必須調?基類的構造函數初始化基類的那?部分成員。如果基類沒有默認的構造函數,則必須在派?類構造函數的初始化列表階段顯?調?。

2. 派?類的拷?構造函數必須調?基類的拷?構造完成基類的拷?初始化。

3. 派?類的operator=必須要調?基類的operator=完成基類的復制。需要注意的是派?類operator=隱藏了基類的operator=,所以顯?調?基類的operator=,需要指定基類作?域

4. 派?類的析構函數會在被調?完成后?動調?基類的析構函數清理基類成員。因為這樣才能保證派?類對象先清理派?類成員再清理基類成員的順序。

5. 派?類對象初始化先調用基類構造再調派?類構造。

6. 派?類對象析構清理先調?派?類析構再調基類的析構。

7. 因為多態中?些場景析構函數需要構成重寫,重寫的條件之?是函數名相同(這個我們多態章節會講解)。那么編譯器會對析構函數名進?特殊處理,處理成destructor(),所以基類析構函數不加virtual的情況下,派?類析構函數和基類析構函數構成隱藏關系。

class Person
{
public://Person(const char* name = "peter")Person(const char* name): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};//子類中父類成員變量當成整體對象,作為子類自定義類型成員看待
class Student : public Person
{
public:// 報錯/*Student(const char* name, int num):_name(name),_num(num){}*/Student(const char* name, int num):Person(name),_num(num){}Student(const Student& s):Person(s),_num(s._num){// 深拷貝 需要自己寫,否則默認生成的就可以夠了//...}Student& operator=(const Student& s){// 深拷貝 需要自己寫,否則默認生成的就可以夠了if (this != &s){Person::operator=(s);_num = s._num;}return *this;}~Student(){// 不需要顯示調用,保證析構時,先子后父//Person::~Person();delete[] _ptr;// 派生類析構結束后,自動調用父類的析構}
protected:int _num = 1; //學號int* _ptr = new int[10];
};

6. 繼承與友元

友元關系不能繼承,也就是說基類友元不能訪問派?類私有和保護成員

class Student;            //前置聲明
class Person
{
public:friend void Display(const Person& p, const Student& s);protected:string _name; // 姓名
};
class Student : public Person
{
public:friend void Display(const Person& p, const Student& s);      //將這行去掉后,編譯器報錯protected:int _stuNum; // 學號
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;
}
int main()
{Person p;Student s;// 編譯報錯:error C2248: “Student::_stuNum”: 無法訪問 protected 成員// 解決方案:Display也變成Student 的友元即可Display(p, s);return 0;
}

7. 繼承與靜態成員

基類定義了static靜態成員,則整個繼承體系里面只有?個這樣的成員。?論派?出多少個派?類,都只有?個static成員實例。

class Person
{
public:string _name;static int _count;
};int Person::_count = 0;class Student : public Person
{
protected:int _stuNum;
};int main()
{Person p;Student s;cout << &p._name << endl;cout << &s._name << endl;cout << &p._count << endl;cout << &s._count << endl;cout << &Person::_count << endl;cout << &Student::_count << endl;return 0;
}

運行結果:

8. 多繼承及其菱形繼承問題

8.1 繼承模型

單繼承:?個派?類只有?個直接基類時稱這個繼承關系為單繼承

多繼承:?個派?類有兩個或以上直接基類時稱這個繼承關系為多繼承,多繼承對象在內存中的? ? ? ? ? ? ? ? ? 模型是,先繼承的基類在前面,后面繼承的基類在后面,派生類成員在放到最后面。

class Person
{
public:string _name; // 姓名
};class Student : virtual public Person
{
protected:int _num; //學號
};class Teacher : virtual public Person
{
protected:int _id; // 職工編號
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修課程
};int main()
{// 編譯報錯:error C2385: 對“_name”的訪問不明確Assistant a;a._name = "peter";// 需要顯示指定訪問哪個基類的成員可以解決二義性問題,但是數據冗余問題無法解決a.Student::_name = "xxx";a.Teacher::_name = "yyy";return 0;
}

?9. 繼承和組合

9.1 繼承和組合

? public繼承是?種is-a的關系。也就是說每個派生類對象都是?個基類對象。

? 組合是?種has-a的關系。假設B組合了A,每個B對象中都有?個A對象。

? 繼承允許你根據基類的實現來定義派?類的實現。這種通過生成派生類的復用通常被稱為白箱復用(white-box reuse)。術語“白箱”是相對可視性而言:在繼承方式中,基類的內部細節對派生類可見?。繼承?定程度破壞了基類的封裝,基類的改變,對派生類有很?的影響。派生類和基類間的依賴關系很強,耦合度高。

? 對象組合是類繼承之外的另?種復用選擇。新的更復雜的功能可以通過組裝或組合對象來獲得。對象組合要求被組合的對象具有良好定義的接口。這種復用風格被稱為黑箱復用(black-box reuse),因為對象的內部細節是不可見的。對象只以“黑箱”的形式出現。 組合類之間沒有很強的依賴關系,耦合度低。優先使用對象組合有助于你保持每個類被封裝。

? 優先使用組合,而不是繼承。實際盡量多去用組合,組合的耦合度低,代碼維護性好。不過也不太那么絕對,類之間的關系就適合繼承(is-a)那就?繼承,另外要實現多態,也必須要繼承。類之間的關系既適合?繼承(is-a)也適合組合(has-a),就用組合。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/71442.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/71442.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/71442.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Golang學習筆記_34——組合模式

Golang學習筆記_31——原型模式 Golang學習筆記_32——適配器模式 Golang學習筆記_33——橋接模式 文章目錄 一、核心概念1. 定義2. 解決的問題3. 核心角色4. 類圖 二、特點分析三、適用場景1. 文件系統2. 圖形界面3. 組織架構 四、代碼示例&#xff08;Go語言&#xff09;五、…

【部署優化篇三】《DeepSeek邊緣計算實戰:把目標檢測模型塞進樹莓派,讓AI在巴掌大的設備上“開天眼“》

“誰說只有超級計算機才能跑AI?今天咱們就要在樹莓派上玩轉DeepSeek目標檢測,讓這個巴掌大的小盒子變成會‘看’世界的智能終端!” 本文手把手教你從零開始,把最潮的目標檢測模型塞進樹莓派。全程高能預警,建議準備好你的樹莓派4B/5和散熱風扇,咱們這就開啟邊緣計算的魔法…

Django中數據庫遷移命令

在 Django 中&#xff0c;數據庫遷移是確保數據庫結構與 Django 模型定義保持一致的重要過程。以下是 Django 中常用的數據庫遷移命令&#xff1a; 1. python manage.py makemigrations 功能&#xff1a;此命令用于根據 Django 項目的模型文件&#xff08;models.py&#xff…

2023年河北省職業院校技能大賽網絡系統管理賽項樣題解法

? 有問題請留言或主頁私信咨詢 配置文件有部分測試時的冗余配置無視即可。 解法只有大致解法&#xff0c;并不完整。請參考配置&#xff0c;自己補全 基礎配置 1.所有交換機和無線控制器開啟SSH服務&#xff0c;用戶名密碼分別為admin、admin1234&#xff1b;密碼為明文類…

React之旅-02 創建項目

創建React項目&#xff0c;常用的方式有兩種&#xff1a; 官方提供的腳手架&#xff0c;官網&#xff1a;https://create-react-app.dev/。如需創建名為 my-app 的項目&#xff0c;請運行如下命令&#xff1a; npx create-react-app my-app 使用Vite包&#xff0c;官網&…

Visual Studio Code的下載安裝與漢化

1.下載安裝 Visual Studio Code的下載安裝十分簡單&#xff0c;在本電腦的應用商店直接下載安裝----注意這是社區版-----一般社區版就足夠用了---另外注意更改安裝地址 2.下載插件 重啟后就是中文版本了

CAN總線常見的錯誤幀及產生原因

文章目錄 一、位錯誤&#xff08;Bit Error&#xff09;1. 表現形式2. 產生原因 二、填充錯誤&#xff08;Stuff Error&#xff09;1. 表現形式2. 產生原因 三、CRC 錯誤&#xff08;CRC Error&#xff09;1. 表現形式2. 產生原因 四、格式錯誤&#xff08;Form Error&#xff…

在項目中調用本地Deepseek(接入本地Deepseek)

前言 之前發表的文章已經講了如何本地部署Deepseek模型&#xff0c;并且如何給Deepseek模型投喂數據、搭建本地知識庫&#xff0c;但大部分人不知道怎么應用&#xff0c;讓自己的項目接入AI模型。 文末有彩蛋哦&#xff01;&#xff01;&#xff01; 要接入本地部署的deepsee…

【擁抱AI】GPT Researcher的誕生

一、GPT Researcher 研究過程總結 GPT Researcher 是一個開源的自主智能體&#xff0c;旨在通過利用人工智能技術實現高效、全面且客觀的在線研究。它通過一系列創新的設計和優化&#xff0c;解決了傳統研究工具&#xff08;如 AutoGPT&#xff09;中存在的問題&#xff0c;如…

52款獨特婚禮主題手繪水彩花卉樹葉高清png免摳圖設計素材Floria – Botanical Elements

Floria 是一個獨特的系列&#xff0c;由郁郁蔥蔥的植物元素組成&#xff0c;以繪畫風格手繪。它們非常適合任何設計項目 – 包裝、網站、博客、文具等等&#xff01; 在 Photoshop 中輕松更改顏色&#xff0c;并將其圖層添加到任何紋理或顏色上。擁有 52 種花卉元素&#xff0c…

python入門筆記4

Python 中的列表&#xff08;List&#xff09;是 有序、可變 的序列類型&#xff0c;用方括號 [] 定義。以下是列表的核心語法和常用操作&#xff1a; list1 [Google, W3Cschool, 1997, 2000] list2 [7, 2, 3, 4, 5, 6, 1 ] #索引操作 print ("list1 first: ", li…

基于Ubuntu+vLLM+NVIDIA T4高效部署DeepSeek大模型實戰指南

一、 前言&#xff1a;擁抱vLLM與T4顯卡的強強聯合 在探索人工智能的道路上&#xff0c;如何高效地部署和運行大型語言模型&#xff08;LLMs&#xff09;一直是一個核心挑戰。尤其是當我們面對資源有限的環境時&#xff0c;這個問題變得更加突出。原始的DeepSeek-R1-32B模型雖…

Linux環境Docker使用代理推拉鏡像

閑扯幾句 不知不覺已經2月中了&#xff0c;1個半月忙得沒寫博客&#xff0c;這篇其實很早就想寫了&#xff08;可追溯到Docker剛剛無法拉鏡像的時候&#xff09;&#xff0c;由于工作和生活上的事比較多又在備考軟考架構&#xff0c;拖了好久…… 簡單記錄下怎么做的&#xf…

撕碎QT面具(6):調節窗口大小后,控件被擠得重疊的解決方法

問題&#xff1a;控件重疊 分析原因&#xff1a;因為設置了最小大小&#xff0c;所以界面中的大小不會隨窗口的變化而自動變化。 處理方案&#xff1a;修改mimumSize的寬度與高度為0&#xff0c;并設置sizePolicy為Expanding&#xff0c;讓其自動伸縮。 結果展示&#xff08;自…

Mysql測試連接失敗

解決方案 1 將mysql.exe(C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe)配置到系統環境變量 2 管理員權限啟動cmd 輸入 3 ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456; 4 FLUSH PRIVILEGES;

【UCB CS 61B SP24】Lecture 3 - Lists 1: References, Recursion, and Lists學習筆記

本文開坑伯克利 CS 61B&#xff08;算法與數據結構&#xff09;2024年春季課程學習筆記&#xff0c;Lecture 1 & Lecture 2 的內容為課程介紹與 Java 基礎&#xff0c;因此直接跳過。本文內容為介紹基本數據類型與引用數據類型的區別&#xff0c;以及手動實現整數列表。 1…

每日學習Java之一萬個為什么

9.Class <?> class1 Myclass.class 為什么要有通配符&#xff1f;傳給誰用的&#xff1f; 首先&#xff0c;這里的class特指某個對象在JVM中的元數據集合。 有普通、接口、數組、基本類型、 void 類型、局部類、匿名類、枚舉、注解 1.類型安全&#xff1a;通配符允許…

【算法】787. 歸并排序

題目 歸并排序 思路 和快排一樣&#xff0c;先判斷數據是否沒有或者只為一個&#xff1b;如果大于一個&#xff0c;取中間的值一分為二&#xff0c;然后兩邊遞歸&#xff0c;歸并的實質是把兩個有序數組排成一個&#xff0c;兩個數組都從頭開始比較&#xff0c;把更小的取下…

濾波器 | 原理 / 分類 / 特征指標 / 設計

注&#xff1a;本文為 “濾波器” 相關文章合輯。 未整理去重。 淺談濾波器之 —— 啥是濾波器 原創 RF 小木匠 射頻學堂 2020 年 03 月 25 日 07:46 濾波器&#xff0c;顧名思義&#xff0c;就是對信號進行選擇性過濾&#xff0c;對不需要的信號進行有效濾除。按照其傳輸信…

DeepSeek-學習與實踐

1.應用場景 主要用于學習與使用DeepSeek解決問題, 提高效率. 2.學習/操作 1.文檔閱讀 文檔 DeepSeek -- 官網, 直接使用 --- 代理網站 --- 極客智坊 https://poe.com/DeepSeek-R1 https://time.geekbang.com/search?qdeepseek -- 搜索deepseek的資料 資料 20250209DeepSeekC…