【C++】繼承:萬字總結

📝前言:
這篇文章我們來講講面向對象三大特性之一——繼承

🎬個人簡介:努力學習ing
📋個人專欄:C++學習筆記
🎀CSDN主頁 愚潤求學
🌄其他專欄:C語言入門基礎,python入門基礎,python刷題專欄,Linux


文章目錄

  • 一,面相對象三大特性
  • 二,繼承
    • 1 大白話講繼承
    • 2 繼承定義格式
      • 2.1 繼承方式的作用
      • 2.2 繼承類模板
        • 2.2.2 需指定類域
        • 2.2.1 按需實例化
    • 3 基類和派生類間的轉換
    • 4 繼承中的作用域
      • 4.1 隱藏規則
    • 5 派生類的默認成員函數
    • 6 實現?個不能被繼承的類
    • 7 友元關系不能繼承
    • 8 靜態成員的繼承
    • 9 多繼承
    • 10 繼承與組合

一,面相對象三大特性

面相對象編程具有三大特性,分別是封裝、繼承和多態:

  • 封裝
    • 概念:將數據和操作數據的方法綁定在一起,組成一個不可分割的整體,即對象。同時,對外部隱藏對象的內部實現細節,只對外提供有限的訪問接口迭代器就是一種封裝,底層不一樣,但是卻能用相似的方法訪問
    • 作用:通過封裝,可以提高代碼的安全性和可維護性。避免外部代碼直接訪問和修改對象的內部數據,防止數據被意外篡改,同時也使得代碼的結構更加清晰,各個模塊的職責更加明確。
  • 繼承
    • 概念允許創建一個新的類(子類),它基于現有的類(父類)進行擴展,子類可以繼承父類的屬性和方法,并且可以在子類中添加自己特有的屬性和方法,或者重寫父類的方法。
    • 作用:繼承實現了代碼的復用,避免了重復編寫相似的代碼。同時,它也體現了面向對象編程中的“is - a”關系,即子類是父類的一種特殊類型,有助于建立清晰的類層次結構,便于對問題域進行建模。
  • 多態
    • 概念:指同一個方法或操作在不同的對象上可以有不同的表現形式。也就是說,不同的子類對象在調用相同的方法時,可能會執行不同的代碼邏輯,產生不同的結果。
    • 作用:多態提高了代碼的靈活性和可擴展性。當需要添加新的功能或修改現有功能時,不需要大量修改客戶端代碼,只需要在相應的子類中進行修改或擴展即可。它使得代碼更加易于維護和升級,同時也增強了代碼的可讀性和可理解性。

二,繼承

1 大白話講繼承

簡單來說,子類繼承父類就是指:子類可以使用父類的成員,并且也可以自己加自己的成員。我們也把父類稱為基類,子類稱為派?類。
示例(下面這個程序是沒有問題的):

class Person
{
public:string name;int age;char sex;
};class Student : public Person
{
public:int st_number;
};int main()
{Student st1;st1.sex = 'b';st1.st_number = 23;return 0;
}

在這里,Person是父類,Student是子類
在這里插入圖片描述
通過監視窗口我們可以看到,st1里面繼承了父類Person的三個成員變量。

2 繼承定義格式

在這里插入圖片描述

2.1 繼承方式的作用

我們都知道,訪問限定符有,privatepublic,和protectedprotected就是專門為繼承設置的。

繼承方式對應也有:privatepublic,和protected

繼承類成員訪問方式的變化
在這里插入圖片描述

  1. 基類private成員在派?類中是不可見的。不可見是語法上限制派?類對象不管在類??還是類外?都不能去訪問它。(但是其實還是被繼承了過去)
  2. protected成員在類外不能直接訪問,在子類中可以被訪問。
  3. 基類的其他成員在派?類的訪問?式 == Min(成員在基類的訪問限定符,繼承方式)public > protected >private
  4. class默認的繼承?式是privatestruct默認的是public,但是建議顯式寫出繼承方式,且一般用public

如,上述代碼中父類改成:

class Person
{
public:string name;private:int age;char sex;
};

這時候子類繼承后,類外執行st1.sex = 'b';就會報錯,因為sex是父類的私有成員

2.2 繼承類模板

繼承類模板需要注意的是:在子類中使用父類類模板的方法時,如果參數是不確定的,要指定一下父類的類域(才能實例化)

2.2.2 需指定類域
namespace tr
{template<class T>class stack: vector<T>{public:void push(T x){push_back(x);}};
}int main()
{tr::stack<int> st;st.push(3);return 0;
}

報錯:
在這里插入圖片描述
原因是:

  1. stack<int>實例化時,也實例化vector<int>了,但是不代表push_back實例化了,因為模板是按需實例化的
  2. 到了push操作,編譯器要對其實例化,但是因為編譯器不知道 push_backvector<T> 里的成員,從而找不到 push_back 這個標識符

正確寫法:

void push(T x)
{vector<T>::push_back(x);
}
2.2.1 按需實例化

示例:

namespace tr
{template<class T>class stack: public vector<T>{public:void push(T x){push_back(x);}void print(){cout << "push_back" << endl;}};
}int main()
{tr::stack<int> st;st.print();return 0;
}

上面代碼是能正常運行的,原因是實例化stack的時候,并不會把所有成員都實例化了,后面調用誰,才實例化誰。

3 基類和派生類間的轉換

對象傳遞
當派生類對象傳遞給基類對象的時候,會進行切片,即:派生類對象中基類部分的數據會被復制到基類對象中,而派生類特有的成員則被 “切掉”(會丟失派生類成員的所有信息)。這時候基類對象不能訪問派生類的成員,調用父類的成員時,結果也是父類對象的。
在這里插入圖片描述
指針/引用傳遞
public繼承的派生類對象的指針/引用 可以賦值基類的指針 / 基類的引用,叫向上傳型,也類似做切片。傳遞后,基類的指針/引用指向派生類,但是只能調用派生類中的基類成員那一部分。
如:

class Person
{
public:string name;int age;char sex;
};class Student : public Person
{
public:int st_number;
};int main()
{Student st1;// 派生類對象賦值給基類的指針/引用Person* p1 = &st1;Person& p2 = st1;// 派生類對象賦值給基類對象(實際上調用的是拷貝構造)Person p3 = st1;return 0;
}

注意:基類的不能賦值給子類(基類的指針或者引?可以通過強制類型轉換賦值給派?類的指針或者引?。但是必須是基類的指針是指向派?類對象時才是安全的。這?基類如果是多態類型,可以使?RTTI(Run-Time TypeInformation)的dynamic_cast 來進?識別后進?安全轉換。)

4 繼承中的作用域

基類和派生類都有獨立的作用域

4.1 隱藏規則

隱藏:派?類和基類中有同名成員(即同名變量或者函數,函數只要同名就算),派?類成員將屏蔽基類對同名成員的直接訪問。(也叫做重定義)
如果要訪問被隱藏的父類成員,可以指定域。基類名::成員

示例:

class Person
{
public:void print(){cout << "Person" << endl;}string name;int age = 10;char sex;
};class Student : public Person
{
public:void print(){cout << "Student" << endl;}int age = 18;int st_number;};int main()
{Student st;cout << "st.age: " << st.age << endl; // 父類的被隱藏st.Person::print(); // 指定父類的域return 0;
}

只要函數同名就會隱藏,如下也是隱藏:
在這里插入圖片描述

5 派生類的默認成員函數

我們可以派生類中的變量看出三種類型:內置類型,自定義類型,來自父類

**在子類繼承父類的時候,父類的成員相當于是最先被聲明的,然后才到子類自己的成員。**所以調用構造的時候也是,先父類的,再子類的

如果繼承多個父類,則先繼承的先聲明。

  1. 派?類中基類的成員,必須調用基類的構造函數來初始化派生類的構造函數會自動調用基類的默認構造,所以,通常,派生類中的成員又有資源申請(需要深拷貝)的時候,我們才需要自己實現構造,拷貝構造和析構也同理)。如果基類沒有默認的構造函數,則必須在派?類構造函數的初始化列表階段顯式調?(基類的構造函數)

示例(子類構造自動調用基類默認構造完成對基類成員的初始化):

class Person
{
public:Person(int age = 10) // 基類有默認構造:name("小紅"),age(age),sex('b'){}void print(){cout << "Person" << endl;}string name;int age;char sex;
};class Student : public Person
{
public:Student(int number) // 子類構造自動調用父類默認構造{st_number = number;}void print(int i){cout << "Student" << endl;}int st_number;};int main()
{Student st(23);return 0;
}

在這里插入圖片描述
當父類沒有默認構造:

class Person
{
public:Person(int a) // 父類沒有默認構造:name("小紅"),age(a),sex('b'){}void print(){cout << "Person" << endl;}string name;int age;char sex;
};class Student : public Person
{
public:Student(int number){st_number = number;}void print(int i){cout << "Student" << endl;}int st_number;
};int main()
{Student st(23);return 0;
}

報錯:
在這里插入圖片描述
正確寫法:

Student(int number, int a):Person(a) // 在初始化列表顯示調用父類的默認構造
{st_number = number;
}
  1. 派?類的拷貝構造函數必須調用基類的拷貝構造完成對基類成員的拷貝初始化(如果這個拷貝構造不是缺省的,即不是默認構造函數,也要放在初始化列表)
  2. 派?類的operator=必須要調用基類的operator完成基類的復制。需要注意的是派?類的
    operator=隱藏了基類的operator=,所以顯?調?基類的operator=,需要指定基類作?域
  3. 派?類的析構函數會在被調?完成后自動調用基類的析構函數清理基類成員。因為這樣才能保證派?類對象先清理派?類成員再清理基類成員(后定義的先清理)的順序
  4. 因為多態中?些場景析構函數需要構成重寫,重寫的條件之?是函數名相同。那么編譯器會對析構函數名進?特殊處理,處理成destructor(),所以基類析構函數不加virtual的情況下,派生類析構函數和基類析構函數構成隱藏關系。如果要顯示調用就要指定域。

示例:

class Person
{
public:Person(int a):name("小紅"),age(a){}Person(const Person& p){name = p.name;age = p.age;}Person& operator=(const Person& p){if (this != &p){name = p.name;age = p.age;}return *this;}~Person(){cout << "~Person()" << endl;}string name;int age;
};class Student : public Person
{
public:Student(int number, int a):Person(a) // 在初始化列表顯示調用父類的默認構造{cout << "Student(int number, int a)" << endl;st_number = number;}// 拷貝構造錯誤寫法:/*Student(const Student& s){Person(s);st_number = s.st_number;cout << "Student(const Student& s)" << endl;}*/// 正確寫法:Student(const Student& s): Person(s){st_number = s.st_number;cout << "Student(const Student& s)" << endl;}Student& operator=(const Student& s){cout << "Student& operator=(const Student& s)" << endl;if (this != &s){Person::operator=(s); // 顯式調用父類的=重載來初始化父類的成員st_number = s.st_number;}return *this;}~Student(){cout << "~Student()" << endl;}int st_number;
};int main()
{Student st1(23, 18);Student st2(st1);Student st3(25, 35);st1 = st3;return 0;
}

運行結果:
在這里插入圖片描述

6 實現?個不能被繼承的類

  • ?法1:基類的構造函數私有,派?類的構成必須調?基類的構造函數,但是基類的構成函數私有化以后,派?類看不見就不能調用了,那么派生類就?法實例化出對象。
  • ?法2:C++11新增了?個final關鍵字,final修改基類(表示最終類),派?類就不能繼承了。

示例:class Person final
在這里插入圖片描述

7 友元關系不能繼承

也就是說基類友元只對基類起作用,而不能訪問派?類私有和保護成員。

8 靜態成員的繼承

基類定義了static靜態成員,則整個繼承體系??只有?個這樣的成員。?論派?出多少個派?類,都只有?個static成員實例,用的是用一塊內存空間的static成員

9 多繼承

  • 單繼承:?個派?類只有?個直接基類

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

  • 菱形繼承:菱形繼承是多繼承的?種特殊情況。菱形繼承的問題,從下?的對象成員模型構造,可以看出菱形繼承有數據冗余和?義性的問題,在Assistant的對象中Person成員會有兩份。
    在這里插入圖片描述

虛擬菱形繼承
在菱形繼承結構中,如果存在多層繼承關系,使得一個派生類從兩個或多個基類中繼承了相同的成員,就會產生數據冗余和二義性問題。

虛擬繼承就是為了解決這種問題而引入的,通過在繼承關系中使用virtual關鍵字,使得在最終的派生類中只保留一份共享的基類子對象。

比如,在上述StudentTeacher繼承時使用虛擬繼承,在可能產生數據冗余和二義性的地方添加virtual,就不會產生兩個name成員,在通過Assistant訪問成員name時,就只有一個,不會產生二義性問題:

如果不添加:

在這里插入圖片描述
添加以后:

class Person
{
public:string _name; // 姓名
// 使?虛繼承Person類
class Student : virtual public Person
{
protected:int _num; //學號
};
// 使?虛繼承Person類
class Teacher : virtual public Person
{
protected:int _id; // 職?編號
};// 教授助理
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修課程
};
int main()
{
// 使?虛繼承,可以解決數據冗余和?義性Assistant a;a._name = "tr";return 0;
}

10 繼承與組合

  • public繼承是?種is-a的關系。也就是說每個派?類對象都是?個基類對象。
  • 組合是?種has-a的關系。假設B組合了A,每個B對象中都有?個A對象。
  • 繼承允許你根據基類的實現來定義派?類的實現。這種通過?成派?類的復?通常被稱為?箱復?。“白箱”:即相對可見。在繼承中,基類的內部細節對派?類可見,基類的改變,對派?類有很?的影響。派?類和基類間的依賴關系很強,耦合度?
  • 對象組合是類繼承之外的另?種復?選擇。新的更復雜的功能可以通過組裝或組合對象來獲得。對象組合要求被組合的對象具有良好定義的接?。這種復??格被稱為?箱復?(black-box reuse),因為對象的內部細節是不可?的。組合類之間沒有很強的依賴關系,耦合度低
  • 優先使?對象組合有助于你保持每個類被封裝,優先使?組合(has - a),?不是繼承(is - 1)

🌈我的分享也就到此結束啦🌈
要是我的分享也能對你的學習起到幫助,那簡直是太酷啦!
若有不足,還請大家多多指正,我們一起學習交流!
📢公主,王子:點贊👍→收藏?→關注🔍
感謝大家的觀看和支持!祝大家都能得償所愿,天天開心!!!

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

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

相關文章

Java 架構設計:從單體架構到微服務的轉型之路

Java 架構設計&#xff1a;從單體架構到微服務的轉型之路 在現代軟件開發中&#xff0c;架構設計的選擇對系統的可擴展性、可維護性和性能有著深遠的影響。隨著業務需求的日益復雜和用戶規模的不斷增長&#xff0c;傳統的單體架構逐漸暴露出其局限性&#xff0c;而微服務架構作…

Django3 - 開啟Django Hello World

一、開啟Django Hello World 要學習Django首先需要了解Django的操作指令&#xff0c;了解了每個指令的作用&#xff0c;才能在MyDjango項目里編寫Hello World網頁&#xff0c;然后通過該網頁我們可以簡單了解Django的開發過程。 1.1 Django的操作指令 無論是創建項目還是創建項…

2025阿里云AI 應用-AI Agent 開發新范式-MCP最佳實踐-78頁.pptx

2025阿里云AI 應用-AI Agent 開發新范式-MCP最佳實踐&#xff0c;包含以下內容&#xff1a; 1、AI 應用架構新范式 2、云原生API網關介紹 3、云原生API網關底座核心優勢 4、流量網關最佳實踐 5、AI 網關代理 LLM 最佳實踐 6、MCP網關最佳實踐 7、MSE Nacos MCP Server 注冊中心…

Pytorch深度學習框架60天進階學習計劃 - 第41天:生成對抗網絡進階(一)

Pytorch深度學習框架60天進階學習計劃 - 第41天&#xff1a;生成對抗網絡進階&#xff08;一&#xff09; 今天我們將深入探討生成對抗網絡(GAN)的進階內容&#xff0c;特別是Wasserstein GAN&#xff08;WGAN&#xff09;的梯度懲罰機制&#xff0c;以及條件生成與無監督生成…

大模型到底是怎么產生的?一文了解大模型誕生全過程

前言 大模型到底是怎么產生的呢? 本文將從最基礎的概念開始,逐步深入,用通俗易懂的語言為大家揭開大模型的神秘面紗。 大家好,我是大 F,深耕AI算法十余年,互聯網大廠核心技術崗。 知行合一,不寫水文,喜歡可關注,分享AI算法干貨、技術心得。 【專欄介紹】: 歡迎關注《…

五子棋(測試報告)

文章目錄 一、項目介紹二、測試用例三、自動化測試用例的部分展示注冊登錄游戲大廳游戲匹配 總結 一、項目介紹 本項目是一款基于Spring、SpringMVC、MyBatis、WebSocket的雙人實時對戰五子棋游戲,游戲操作便捷&#xff0c;功能清晰明了。 二、測試用例 三、自動化測試用例的…

idea開發工具多賬號使用拉取代碼報錯問題

設置git不使用憑證管理 把 use credential helper 取消勾選 然后重新pull代碼&#xff0c;并勾選remember 這樣就可以使用多賬號來連接管理代碼了

【OpenCV】【XTerminal】talk程序運用和linux進程之間通信程序編寫,opencv圖像庫編程聯系

目錄 一、talk程序的運用&Linux進程間通信程序的編寫 1.1使用talk程序和其他用戶交流 1.2用c語言寫一個linux進程之間通信&#xff08;聊天&#xff09;的簡單程序 1.服務器端程序socket_server.c編寫 2.客戶端程序socket_client.c編寫 3.程序編譯與使用 二、編寫一個…

【軟考系統架構設計師】信息系統基礎知識點

1、 信息的特點&#xff1a;客觀性&#xff08;真偽性&#xff09;、動態性、層次性、傳遞性、滯后性、擴壓性、分享性 2、 信息化&#xff1a;是指從工業社會到信息社會的演進與變革 3、 信息系統是由計算機硬件、網絡和通信設備、計算機軟件、信息資源、信息用戶和規章制度…

一種基于學習的多尺度方法及其在非彈性碰撞問題中的應用

A learning-based multiscale method and its application to inelastic impact problems 摘要&#xff1a; 我們在工程應用中觀察和利用的材料宏觀特性&#xff0c;源于電子、原子、缺陷、域等多尺度物理機制間復雜的相互作用。多尺度建模旨在通過利用固有的層次化結構來理解…

基于PyQt5的Jupyter Notebook轉Python工具

一、項目背景與核心價值 在數據科學領域,Jupyter Notebook因其交互特性廣受歡迎,但在生產環境中通常需要將其轉換為標準Python文件。本文介紹一款基于PyQt5開發的桌面級轉換工具,具有以下核心價值: 可視化操作:提供友好的GUI界面,告別命令行操作 批量處理:支持目錄遞歸…

圖論之并查集——含例題

目錄 介紹 秩是什么 例子——快速入門 例題 使用路徑壓縮&#xff0c;不使用秩合并 使用路徑壓縮和秩合并 無向圖和有向圖 介紹 并查集是一種用于 處理不相交集合的合并與查詢問題的數據結構。它主要涉及以下基本概念和操作&#xff1a; 基本概念&#xff1a; 集合&…

【數學建模】(智能優化算法)天牛須算法(Beetle Antennae Search, BAS)詳解與Python實現

天牛須算法(Beetle Antennae Search, BAS)詳解與Python實現 文章目錄 天牛須算法(Beetle Antennae Search, BAS)詳解與Python實現1. 引言2. 算法原理2.1 基本思想2.2 數學模型 3. Python實現4.實測效果測試1. Michalewicz函數的最小化測試2. Goldstein-Price函數的約束最小化 5…

【家政平臺開發(42)】筑牢家政平臺安全防線:安全測試與漏洞修復指南

本【家政平臺開發】專欄聚焦家政平臺從 0 到 1 的全流程打造。從前期需求分析,剖析家政行業現狀、挖掘用戶需求與梳理功能要點,到系統設計階段的架構選型、數據庫構建,再到開發階段各模塊逐一實現。涵蓋移動與 PC 端設計、接口開發及性能優化,測試階段多維度保障平臺質量,…

學習筆記八——內存管理相關

&#x1f4d8; 目錄 內存結構基礎&#xff1a;棧、堆、數據段Rust 的內存管理機制&#xff08;對比 C/C、Java&#xff09;Drop&#xff1a;Rust 的自動清理機制Deref&#xff1a;為什么 *x 能訪問結構體內部值Rc&#xff1a;多個變量“共享一個資源”怎么辦&#xff1f;Weak&…

ReliefF 的原理

&#x1f31f; ReliefF 是什么&#xff1f; ReliefF 是一種“基于鄰居差異”的特征選擇方法&#xff0c;用來評估每個特征對分類任務的貢獻大小。 它的核心問題是&#xff1a; “我怎么知道某個特征是不是重要&#xff1f;是不是有能力把不同類別的數據區分開&#xff1f;” 而…

?asm匯編源代碼之-漢字點陣字庫顯示程序源代碼下載?

漢字點陣字庫顯示程序 源代碼下載 文本模式下顯示16x16點陣漢字庫內容的程序(標準16x16字庫需要使用CHGHZK轉換過后才能使用本程序正常顯示) 本程序需要調用file.asm和string.asm中的子程序,所以連接時需要把它們連接進來,如下 C:\> tlink showhzk file string 調用參…

【已更新完畢】2025泰迪杯數據挖掘競賽B題數學建模思路代碼文章教學:基于穿戴裝備的身體活動監測

基于穿戴裝備的身體活動監測 摘要 本研究基于加速度計采集的活動數據&#xff0c;旨在分析和統計100名志愿者在不同身體活動類別下的時長分布。通過對加速度數據的處理&#xff0c;活動被劃分為睡眠、靜態活動、低強度、中等強度和高強度五類&#xff0c;進而計算每個志愿者在…

Ubuntu24.04裝機安裝指南

文章目錄 Ubuntu24.04裝機安裝指南一、分區說明二、基礎軟件三、使用fcitx5配置中文輸入法四、安裝搜狗輸入法【**不推薦**】1. 安裝fcitx2. 安裝輸入法 五、禁用/home目錄下自動生成文件夾六、更新軟件源1. 針對**新配置方式**的清華源替換方法2. 針對**老配置方式**的清華源替…

互聯網三高-數據庫高并發之分庫分表ShardingJDBC

1 ShardingJDBC介紹 1.1 常見概念術語 ① 數據節點Node&#xff1a;數據分片的最小單元&#xff0c;由數據源名稱和數據表組成 如&#xff1a;ds0.product_order_0 ② 真實表&#xff1a;再分片的數據庫中真實存在的物理表 如&#xff1a;product_order_0 ③ 邏輯表&#xff1a…