C++函數繼承

C++函數繼承

引言

C++三大特征分別為封裝,繼承和多態,它們構成了面向對象編程的基石,它們協同工作以提升代碼的模塊化,可復用性和靈活性

  • 封裝:提高代碼的維護性(當程序出現問題時可以準確定位)

  • 繼承:提高代碼的復用性(在不做任何修改或者操作源碼就能實現代碼的復用)

  • 多態:提高代碼的擴展性(后期文章會介紹)

組合

C++中實現代碼復用操作主要有兩種,分別是組合和繼承,二者各有優缺點

#include <iostream>using namespace std;?class A{public:void func(){cout << "Hello world" << endl;}int m_num;};?class B{public:?//創建A類成員A a;?void func(){//此時B包含Aa.func();}};?int main(){B b;b.func();?return 0;}

執行結果:

上述代碼就是一個典型的組合操作的實現,B包含A對象作為成員變量,這樣B就可以調用A中的方法,此時修改A的內部邏輯并不會影響B(只要接口不變),操作時也更加直接,可以組合多個類,操作方法都是通過成員對象來訪問,這樣就在不復制代碼內容的情況下使用了其他類中的功能。但這種方法每次組合一個類就要多創建一個成員,此時會增大內存占用。

繼承

繼承同樣在代碼復用方面起著重要作用

#include <iostream>using namespace std;?class A{public:void print(){cout << "Hello world" << endl;}int m_num;};?class B : public A{public:?//繼承之后無需聲明A類成員便可直接調用A類內的共有函數void func(){print();}};?int main(){A a;B b;b.func();?//繼承了A類之后便可以直接使用與A相同的成員b.m_num = 10;b.print();?cout << b.m_num << endl;cout << a.m_num << endl;?cout << sizeof(b) << endl;?return 0;}?

輸出結果

在這里與組合相比,繼承就可以減少內存開銷,繼承之后B稱為A的子類或派生類,A稱為B的父類。派生類的實例化對象大小:父類的對象大小 + 派生類的新成員;與此同時我們還應該注意繼承的空間分配問題,繼承后派生類和父類并不公用內存,派生類會在繼承后在自己的內存空間內開辟新的內存來存放與父類不同的成員,大致原理如下

根據程序輸出結果也不難看出修改了B類成員后A類成員并沒有一同發生改變

覆蓋

#include <iostream>using namespace std;?class A{public:void print(){cout << "Hello world" << endl;}int m_num = 12;};?class B : public A{public://與父類重名的成員會在派生類中重新創建int m_num;int count;// 繼承之后無需聲明A類成員便可直接調用A類內的共有函數void func(){print();}?//與父類重名的函數會發生覆蓋void print(){cout << "Hi world" << endl;}};?int main(){A a;B b;b.func();?b.m_num = 10;b.print();?cout << sizeof(b) << endl;?cout << b.m_num << endl;cout << a.m_num << endl;?return 0;}

輸出結果

根據輸出結果不難推理出派生類中的重名函數以及重名成員會覆蓋子類中的對應函數和成員

三種繼承方式

公有繼承

基類的公有成員和屬性成為派生類的共有;基類的被保護的屬性和方法成為派生類的被保護;基類私有成員不能被繼承

保護繼承

基類的公有成員和屬性成為派生類的私有;基類的被保護的屬性和方法成為派生類的私有;基類私有成員不能被繼承

私有繼承

基類的公有成員和屬性成為派生類的被保護的;基類的被保護的屬性和方法成為派生類的被保護的;基類私有成員不能被繼承

三種繼承方式操作方法基本相同,只是權限控制不同,由于我們使用繼承的出發點是實現代碼復用,所以三種繼承方式中使用最多的就是公有繼承

繼承后函數的執行順序

#include <iostream>using namespace std;?class A{public:A(){cout << "A" << endl;}explicit A(int num) : m_num(num){cout << "A int" << endl;}?~A(){cout << "~A" << endl;}int m_num;};?class B : public A{public://使用初始化列表,調用A類的帶參構造函數,如果不使用初始化列表則調用無參構造函數B() : A(5){cout << "B" << endl;}?~B(){cout << "~B" << endl;}?int m_count;};?int main(){B b;cout << b.m_num << endl;return 0;}

輸出結果

由輸出結果不難看出,繼承之后構造函數的執行順序為先調用基類的構造函數然后再派生類的構造函數,析構函數則相反。

同時派生類繼承的屬性需要基類的構造函數進行初始化;如果沒有無參構造函數,那么派生類里所有的構造函數都要顯示調用基類的構造函數

子對象的構造順序

當一個類中聲明而多個子對象時它們的構造函數調用順序又是怎樣的呢

#include <iostream>using namespace std;?class Test{public:?Test(int index) : m_index(index){cout << "Test int" << endl;}?~Test(){cout << "~Test" << endl;}?int m_index;};?class Test1{public:?Test1(int index) : m_index(index){cout << "Test1 int" << endl;}?~Test1(){cout << "~Test1" << endl;}?int m_index;};?class B{public://通過初始化列表來初始化對象//子對象在初始化列表中的順序B() : t1(0),t(0){cout << "B" << endl;}?~B(){cout << "~B" << endl;}?int m_count;?//子對象的聲明順序Test t;Test1 t1;};?int main(){B b;?return 0;}

輸出結果

根據上述代碼可以推出子對象構造函數的調用順序與子對象的聲明順序相關。先聲明的對象先構造

多基類的構造順序

#include <iostream>using namespace std;?class A{public:?A(int index) : m_index(index){cout << "A int" << endl;}?~A(){cout << "~A" << endl;}?int m_index;};?class B{public:?B(int index) : m_index(index){cout << "B int" << endl;}?~B(){cout << "~B" << endl;}?int m_index;};?//繼承順序class C : public A, public B{public://通過初始化列表來初始化對象//列表順序C() : B(0),A(0){cout << "C" << endl;}?~C(){cout << "~B" << endl;}?int m_count;?};?int main(){C c;?return 0;}

輸出結果

根據輸出結果不難看出當繼承了多個基類時基類的構造順序由繼承順序決定

二義性問題

多繼承會產生二義性問題,具體形式如下、

直接指定路徑

當我們想要在D類內訪問a時編譯器就會面臨兩個選擇,要么通過類B訪問,要么通過類C訪問,此時就會產生多繼承問題

代碼實現如下

#include <iostream>?using namespace std;//基類Aclass A{public:A(int a) : m_a(a){cout << "A" << endl;}int m_a;};?//基類B繼承Aclass B : public A{public:B(int b) : m_b(b),A(1){cout << "B" << endl;}int m_b;};?//基類C繼承Aclass C : public A{public:C(int c) : m_c(c),A(2){cout << "C" << endl;}int m_c;};?//派生類D同時繼承B和Cclass D : public B,public C{public:D(int d) : m_d(d), B(2), C(3){cout << "D" << endl;}int m_d;};?int main(){D d(4);?//引發報錯,二義性問題導致編譯器我無法確定訪問路徑cout << d.m_a << endl;return 0;}

輸出結果:

此時解決問題的方法由兩種,一種是直接指明路徑將輸出語句內容更換為cout << d.C::m_a << endl;,此時程序就能正常輸出

這種直接指定路徑的方法就可以解決二義性的問題,但是通過輸出結果我們發現A被構造了兩次,因此相比于這種方法我們會傾向于選擇使用虛繼承來解決問題

虛繼承

#include <iostream>?using namespace std;class A{public:A(int a) : m_a(a){cout << "A" << endl;}int m_a;};?//此時B和C采用虛繼承class B : virtual public A{public:B(int b) : m_b(b),A(1){cout << "B" << endl;}int m_b;};?class C : virtual public A{public:C(int c) : m_c(c),A(2){cout << "C" << endl;}int m_c;};?class D : public B,public C{public:D(int d) : m_d(d), B(2), C(3), A(1){cout << "D" << endl;}int m_d;};?int main(){D d(4);?cout << d.C::m_a << endl;return 0;}

輸出結果

此時我們通過觀察發現程序中A類只被構造了一次,在D類中實現的構造,同時需要注意的是當使用虛繼承時B和C都要采用虛繼承,如果只有一個采用了虛繼承那么還是會引發報錯,因為此時A被構造了兩次,又產生了二義性問題

組合和繼承的選擇

當兩者之間是has-a(例如:學生有書包,汽車有發動機)關系時通常使用組合方式,當兩者關系為is-a關系(例如:老師是人,學生也是人)時使用繼承方式

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

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

相關文章

瞬態數據表定義Fluent變量

重要說明&#xff1a;本文基于2025R2版本編寫&#xff0c;其他版本可能存在差異。1 概述瞬態數據表是定義 Fluent 變量隨時間變化規律的profile文件&#xff0c;文件類型為文本文件。瞬態數據表假設所有時刻&#xff0c;被定義的對象都是均勻分布&#xff0c;無法考慮變量在空間…

文本嵌入模型的本質

這是一個非常深刻且重要的問題。我們來詳細拆解一下“通用文本嵌入模型”的本質。 我們可以從三個層次來理解它&#xff1a;它是什么&#xff08;What&#xff09;&#xff0c;它如何工作&#xff08;How&#xff09;&#xff0c;以及它為什么重要&#xff08;Why&#xff09;。…

Linux筆記13——shell編程基礎-7

補充1.printf %s\t%s 字符串 中&#xff0c;\t一定不要加雙引號&#xff0c;這一點和在awk中使用的時候有所不同2.其中%s也可以寫成%ns&#xff0c;n可以被用來設置列寬&#xff0c;默認右對齊#打印輸出文件系統的使用情況 [rootlocalhost ~]# printf %-30s\t%s\n $(df -h | aw…

【混合開發】Android+WebView視頻圖片播放硬件加速詳解

webview視頻播放出現白屏、藍屏、花屏、黑屏等等 但由于布局結構是androidwebviewH5本地視頻等。視頻播放導致的異常排查起來十分復雜且沒有原生的相關日志 于是需要給webview播放視頻進行硬件加速&#xff0c;剛開始以為是一件很簡單的配置而已。本著無經驗從頭開始的原則&am…

Allegro-DDR3實戰-差分對-等長設置-區域規則

本章內容&#xff1a; 一&#xff09;Allegro之DDR3設計 (實操干貨) 二&#xff09;規則設置具體步驟 DDR3信號表: (eg&#xff1a;鎂光MT41J256M16HA-15E) 數據信號 DQ[15:0] DQS[1:0] DM[1:0] DQ:雙向數據總線 DQS:數據選通&#xff0c;用于同步數據傳…

七牛云OSS空間復制遷移到另外一個空間

創新新的空間時存儲地區必須一致 訪問控制必須選擇公開 1、下載七牛的同步工具并解壓 qshell&#xff08;http://developer.qiniu.com/docs/v6/tools/qshell.html&#xff09; 2、解壓文件 3、運行cmd登錄到七牛賬號 qshell account 你的七牛AK 你的七牛SK 你的賬號 4、測…

windows中Qwen3?Coder 與 Claude Code 搭配使用

claude安裝命令 npm install -g anthropic-ai/claude-code環境變量配置 set ANTHROPIC_BASE_URLhttps://dashscope.aliyuncs.com/api/v2/apps/claude-code-proxy set ANTHROPIC_AUTH_TOKENyour-dashscope-apikey可能還需要配置自己的git環境變量 查看git安裝位置 按下Win S打…

thunar 文件管理器實現雙擊使用 nvim打開

archlinux 中thunar 文件管理器&#xff0c;如何實現雙擊使用 nvim打開查看。我用的是kitty 終端。 在 Arch Linux Thunar kitty nvim 的環境里&#xff0c;要實現 雙擊文件 -> 用 nvim 打開&#xff0c;你可以這樣配置&#xff1a;設置為默認應用 如果你想 雙擊直接用 n…

深度學習----卷積神經網絡實現數字識別

一、準備工作 導入庫&#xff0c;導入數據集&#xff0c;劃分訓練批次數量&#xff0c;規定訓練硬件&#xff08;這部分 import torch from torch import nn # 導入神經網絡模塊 from torch.utils.data import DataLoader # 數據包管理工具&#xff0c;打包數據 from torch…

鴻蒙Harmony-從零開始構建類似于安卓GreenDao的ORM數據庫(四)

目錄 一,查詢表的所有數據 二,根據條件查詢數據 三,數據庫升級 前面章節已經講解了數據庫的創建,表的創建,已經增刪改等操作。下面我們來講解一下數據庫的查詢以及升級操作。 一,查詢表的所有數據 先來看看官方文檔: query(predicates: RdbPredicates, callback: Asy…

20250829_編寫10.1.11.213MySQL8.0異地備份傳輸腳本+在服務器上創建cron任務+測試成功

0.已知前提條件: 10.1.11.213 堡壘機訪問 mysql 8.0 版本 密碼在/root/.my.cnf 備份腳本:/data/backup_mysql/mysql_backup.sh alarm_system:動環數據庫 exit_and_entry:出入境數據庫 logs:備份日志 project_cg_view_prod:采購跟蹤系統 all :數據庫整體備份 imip_ecb…

PostgreSQL 流復制與邏輯復制性能優化與故障切換實戰經驗分享

PostgreSQL 流復制與邏輯復制性能優化與故障切換實戰經驗分享 在高可用和數據安全愈發受到重視的生產環境中&#xff0c;PostgreSQL 復制技術是保障業務連續性的重要手段。本文結合真實生產場景&#xff0c;分享流復制&#xff08;Physical Replication&#xff09;與邏輯復制&…

Django開發規范:構建可維護的AWS資源管理應用

引言 在現代Web開發中,遵循一致的開發規范對于項目的可維護性和團隊協作至關重要。本文基于實際的AWS資源管理項目,分享一套經過實踐檢驗的Django開發規范,涵蓋模型設計、Admin配置、管理命令和工具類開發等方面。 模型開發規范 數據模型設計原則 良好的數據模型設計是應…

機器學習可解釋庫Shapash的快速使用教程(五)

文章目錄1 快速使用1.1 安裝1.2 三個簡單步驟快速入門1.2.1 步驟 1&#xff1a;準備模型和數據1.2.2 步驟 2&#xff1a;聲明并編譯 SmartExplainer1.2.3 步驟 3&#xff1a;可視化和探索1.2.4 啟動 Web 應用1.2.5 將解釋結果導出為數據2 Shapash的后端集成2.1 方法一&#xff…

如何在emacs中添加imenu插件

在配置文件中添加&#xff1a; ;; 刪除現有的包管理器配置&#xff08;如果有&#xff09;&#xff0c;然后添加以下&#xff1a;;; 初始化包管理器 (require package);; 清除現有的倉庫列表 (setq package-archives nil);; 添加正確的倉庫&#xff08;注意&#xff1a;使用 H…

Linux下的網絡編程SQLITE3詳解

常用數據庫關系型數據庫將復雜的數據結構簡化為二維表格形式大型&#xff1a;Oracle、DB2中型&#xff1a;MySql、SQLServer小型&#xff1a;Sqlite非關系型數據庫以鍵值對存儲&#xff0c;且結構不固定JSONRedisMongoDBsqlite數據庫特點開源免費&#xff0c;C語言開發代碼量少…

適配openai

openai 腳本 stream腳本import os from openai import OpenAIclient OpenAI(base_url"http://127.0.0.1:9117/api/v1",api_keyos.environ["ACCESS_TOKEN"], )stream client.chat.completions.create(model "Qwen/Qwen2-7B-Instruct",messages…

一天認識一個神經網絡之--CNN卷積神經網絡

CNN 是一種非常強大的深度學習模型&#xff0c;尤其擅長處理像圖片這樣的網格結構數據。你可以把它想象成一個系統&#xff0c;它能像我們的大腦一樣&#xff0c;自動從圖片中學習并識別出各種特征&#xff0c;比如邊緣、角落、紋理&#xff0c;甚至是更復雜的物體部分&#xf…

13 SQL進階-InnoDB引擎(8.23)

一、邏輯存儲結構&#xff08;1&#xff09;表空間&#xff08;ibd文件&#xff09;&#xff1a;一個mysql實例可以對應多個表空間&#xff0c;用于存儲記錄、索引等數據。cd /var/lib/mysql&#xff08;2&#xff09;段&#xff0c;分為數據段&#xff08;leaf node segment&a…

MTK Linux DRM分析(二十四)- MTK mtk_drm_plane.c

一、代碼分析 mtk_drm_plane.h 和 mtk_drm_plane.c 兩個文件,并生成基于文本的函數調用圖,我將首先解析文件中的主要函數及其功能,然后根據代碼中的調用關系整理出調用圖。由于文件內容較長,我會專注于關鍵函數及其相互調用關系,并以清晰的文本形式呈現。 文件分析 1. …