[c++] 繼承和多態整理一

1 private 和 protected 繼承,子類指針不能賦值給父類指針

如下代碼,有一個基類 Base,Derived1,Derived2,Derived3 3 個子類繼承了基類 Base,分別是 private 繼承,protected 繼承,public 繼承。在 main 函數里,分別使用 new 來創建 3 個子類,將 3 個子類指針賦值給 Base 指針,private 和 protected 繼承的時候,子類指針無法賦值給父類指針。

private 繼承或者 protected 繼承下,父類的屬性和函數在子類中的權限發生了變動。所以使用父類指針指向子類,可能會導致不安全的行為。

#include <iostream>
#include <string>class Base {
public:Base() {std::cout << "Base()" << std::endl;}~Base() {std::cout << "~Base()" << std::endl;}virtual void Do() {std::cout << "Base() Do()" << std::endl;}
};class Derived1 : private Base {
public:Derived1() {std::cout << "Derived1()" << std::endl;}~Derived1() {std::cout << "~Derived1()" << std::endl;}
};class Derived2 : protected Base {
public:Derived2() {std::cout << "Derived2()" << std::endl;}~Derived2() {std::cout << "~Derived2()" << std::endl;}
};class Derived3 : public Base {
public:Derived3() {std::cout << "Derived3()" << std::endl;}~Derived3() {std::cout << "~Derived3()" << std::endl;}
};int main() {Base *b1 = new Derived1();Base *b2 = new Derived2();Base *b3 = new Derived3();b1->Do();b2->Do();b3->Do();return 0;
}

編譯結果如下:

2 子類和父類中的同名函數

在類繼承中,一般子類可以覆蓋父類中的虛函數,這是我們使用繼承和多態常用的方式。如果子類中的函數和父類中的函數同名,并且這個函數不是虛函數呢,父類指針調用函數的時候調用的是父類中的函數,還是子類中的函數 ?在實際開發中,一般不會這么使用,這樣的代碼是毫無意義的,是自相矛盾的,因為繼承了父類,那么就是要復用父類中的方法與屬性,即使要覆蓋也應該覆蓋虛函數,如果不是虛函數,那么顯得不倫不類。這里只是對這種情況做一個探討。

#include <iostream>
#include <string>class Base {
public:Base() {std::cout << "Base()" << std::endl;}~Base() {std::cout << "~Base()" << std::endl;}void Do() {std::cout << "Base() Do()" << std::endl;}virtual void VDo() {std::cout << "Base() VDo()" << std::endl;}
};class Derived : public Base {
public:Derived() {std::cout << "Derived()" << std::endl;}~Derived() {std::cout << "~Derived()" << std::endl;}void Do() {std::cout << "Derived() Do()" << std::endl;}void VDo() {std::cout << "Derived() VDo()" << std::endl;}
};int main() {std::cout << "----------------" << std::endl;Base *b = new Derived();b->Do();b->VDo();std::cout << "----------------" << std::endl;Base b1 = *(new Derived);b1.Do();b1.VDo();std::cout << "----------------" << std::endl;Base &b2 = *(new Derived);b2.Do();b2.VDo();return 0;
}

Derived 和 Base 之間的賦值,不管是指針賦值,引用賦值還是值賦值,Base 指針,引用,值調用的都是 Base 中的 Do() 函數;Base 指針和引用調用的是 Derived 中的 VDo(),Base 值調用的是 Base 中的 VDo。

3 編譯器默認生成的函數

面向對象的語言中 =、& 這么基礎的運算符也是需要函數來實現的,這就是 c++ 的特點,一切皆對象。這個在 c 語言中很難理解的。

如果聲明了一個對象,這個對象中什么也沒有聲明,那么為了使用這些基礎的運算,編譯器會默認生成一些函數。

構造函數類的構造
拷貝構造函數拷貝構造
析構函數析構
賦值運算符賦值
取地址運算符取值
取值運算符取地址
#include <iostream>
#include <string>class Base {
};int main() {Base b1;Base b2;b2 = b1;Base b3 = b2;std::cout << "&b1 = " << &b1 << std::endl;std::cout << "&b2 = " << &b2 << std::endl;return 0;
}

c++ 默認生成的函數,比如構造函數,如果我們在定義類的時候定義了自己的構造函數,那么編譯器就不會生成默認的了。

如下代碼中,Base 是一個基類,Derived 繼承了 Base 類。在 Base 類中聲明了構造函數 Base(int i),這樣就不會有默認構造函數 Base() 了。在 Derived 類構造時,沒有顯式調用 Base 的構造函數,那么就會調用默認構造函數 Base(),但是默認構造函數又不存在,所以在編譯的時候就會報錯。需要在 Derived 構造列表中對 Base 進行構造,比如 Base(10)。

#include <iostream>
#include <string>class Base {
public:Base(int i) : i_(i) {std::cout << "Base(), i = " << i_ << std::endl;}private:int i_;
};class Derived : public Base {
public:Derived() {std::cout << "Derived(), i " << Base::i_ << std::endl;}
};int main() {Derived d;return 0;
}

4 私有繼承,繼承和組合

在討論設計模式的時候,有一個原則經常被提到: 多用組合,少用繼承,能用組合就用組合,萬不得已的時候才使用繼承。

私有繼承的兩個特點:

(1)私有繼承的派生類指針不能賦值給基類指針

(2)私有繼承之后,基類中的 public 和 protected 成員,在子類中都變成了 private 屬性,不能通過對象來訪問

比如對于汽車這個對象,包括汽車發動機,懸架,轉向 3 個主要的系統,我們在描述汽車的時候,如果使用私有繼承的方式繼承了發動機,懸架,轉向 3 個類,是可以實現的,但是從語義上是不通的;使用組合的方式,在語義上是相通的,因為汽車是由這 3 個對象組合而成的。

如下是一個私有繼承和組合的例子,Steering 表示轉向系統,BydCar 私有繼承了 Steering 類,TeslaCar 使用的組合。

#include <iostream>
#include <string>class Steering {
public:Steering() {std::cout << "Steering()" << std::endl;}~Steering() {std::cout << "~Steering()" << std::endl;}void Turn() {std::cout << "Steering() Turn()" << std::endl;}
};class BydCar : private Steering {
public:void Turn() {std::cout << "BydCar() Turn()" << std::endl;Steering::Turn();}
};class TeslaCar {
private:Steering steer;public:void Turn() {std::cout << "TeslaCar() Turn()" << std::endl;steer.Turn();}
};int main() {BydCar byd;TeslaCar tesla;byd.Turn();tesla.Turn();return 0;
}

使用組合的時候,一般組合的各個子元素都是已經實現的最終的結果,組合的元素是一個基本的元素,組合之后不能對這些基本元素進行修改,并且不能訪問元素中的私有成員;使用繼承的時候,基類可以是一個抽象類,派生類中也可以重寫基類的函數。

設計原則中有一個是對修改關閉,對擴展開開放。這兩個方面和繼承與組合也有一定的對應關系,組合相當于擴展,繼承可以修改,對組合開放相當于優先選用組合,對修改關閉相當于少用繼承。

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

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

相關文章

基于springboot+vue的紡織品企業財務管理系統

博主主頁&#xff1a;貓頭鷹源碼 博主簡介&#xff1a;Java領域優質創作者、CSDN博客專家、阿里云專家博主、公司架構師、全網粉絲5萬、專注Java技術領域和畢業設計項目實戰&#xff0c;歡迎高校老師\講師\同行交流合作 ?主要內容&#xff1a;畢業設計(Javaweb項目|小程序|Pyt…

Socket網絡編程(五)——TCP數據發送與接收并行

目錄 主要實現需求TCP 服務端收發并行重構啟動main方法重構重構分離收發消息的操作重構接收消息的操作重構發送消息TCPServer調用發送消息的邏輯監聽客戶端鏈接邏輯重構Socket、流的退出與關閉 TCP 客戶端收發并行重構客戶端 main函數重構客戶端接收消息重構客戶端發送消息重構…

前端封裝通用下載方法及下載后端返回的文件流

目錄 1.下載方法封裝 2.將后端返回的文件流轉換為文件 3.總結 1.下載方法封裝 ①說明 前端的請求大概分為三種類型 普通請求&#xff1a;常用的get&#xff0c;post&#xff0c;put&#xff0c;delete等請求 上傳請求&#xff1a;使用post請求&#xff0c;發送formdata對…

Zookeeper學習1:概述、安裝、應用場景、集群配置

文章目錄 概述安裝LinuxWindows 配置參數集群參考配置文件配置步驟流程啟動 概述 Zookeeper&#xff1a; 為分布式框架組件提供協調服務的中間件 【類似&#xff1a;文件系統通知機制】 負責存儲上下層應用關系的數據以及接收觀察者注冊監聽&#xff0c;一旦觀察查關心的數據發…

git操作基本指令

1.查看用戶名 git config user.name 2.查看密碼 git config user.password 3.查看郵箱 git config user.email 4.修改用戶名 git config --global user.name "xxx(新用戶名)" 5.修改密碼 git config --global user.password "xxx(新密碼)" 6.修改…

筆記73:ROS中的各種消息包

參考視頻&#xff1a; 33.ROS 的標準消息包 std_msgs_嗶哩嗶哩_bilibili 34. ROS 中的幾何包 geometry_msgs 和 傳感器包 sensor_msgs_嗶哩嗶哩_bilibili 標準消息包&#xff1a;std_msgs常用消息包&#xff1a;common_msgs導航消息包&#xff1a;nav_msgs幾何消息包&#xf…

實戰分享:Tomcat打破雙親委派模型,實現Web應用獨立與安全隔離的奧秘

目錄 一、JVM 類加載機制 二、Tomcat 類加載器 2.2 findClass 介紹 3.2 loadClass 介紹 三、web應用隔離 3.1 Spring 加載問題 在開始文章內容之前&#xff0c;先來看三個問題 假如在 Tomcat 上運行了兩個 Web 應用程序&#xff0c;兩個 web 應用中有同名的Servlet&#xf…

C++數據結構與算法——二叉樹的屬性

C第二階段——數據結構和算法&#xff0c;之前學過一點點數據結構&#xff0c;當時是基于Python來學習的&#xff0c;現在基于C查漏補缺&#xff0c;尤其是樹的部分。這一部分計劃一個月&#xff0c;主要利用代碼隨想錄來學習&#xff0c;刷題使用力扣網站&#xff0c;不定時更…

AGI概念與實現

AGI AGI&#xff08;Artificial General Intelligence&#xff09;&#xff0c;中文名為“通用人工智能”或“強人工智能”&#xff0c;是指通過機器學習和數據分析等技術&#xff0c;使計算機具有類似于人類的認知和學習能力的技術. 多模態的大模型 &#xff08;Multimodal…

詳細介紹如何用windows自帶Hyper-V安裝虛擬機(windows11和ubuntu22)

通過系統自帶的hyper-v安裝windows11&#xff0c;舒服又愜意&#xff0c;相比用第三方虛擬機軟件速度快很多。 硬件準備 準備 系統需要符合能安裝 Hyper-V 的最低要求windows版本含Hyper-V的功能 電腦空間 電腦要有足夠的空間來安裝你這個虛擬機。根據自己的磁盤容量情況來規…

2673. 使二叉樹所有路徑值相等的最小代價

給你一個整數 n 表示一棵 滿二叉樹 里面節點的數目&#xff0c;節點編號從 1 到 n 。根節點編號為 1 &#xff0c;樹中每個非葉子節點 i 都有兩個孩子&#xff0c;分別是左孩子 2 * i 和右孩子 2 * i 1 。 樹中每個節點都有一個值&#xff0c;用下標從 0 開始、長度為 n 的整…

CloudCanal x Hive 構建高效的實時數倉

簡述 CloudCanal 最近對于全周期數據流動進行了初步探索&#xff0c;打通了Hive 目標端的實時同步&#xff0c;為實時數倉的構建提供了支持&#xff0c;這篇文章簡要做下分享。 基于臨時表的增量合并方式基于 HDFS 文件寫入方式臨時表統一 Schema任務級的臨時表 基于臨時表的…

【Linux實踐室】Linux初體驗

&#x1f308;個人主頁&#xff1a;聆風吟 &#x1f525;系列專欄&#xff1a;Linux實踐室、網絡奇遇記 &#x1f516;少年有夢不應止于心動&#xff0c;更要付諸行動。 文章目錄 一. ??任務描述二. ??相關知識2.1 &#x1f514;Linux 目錄結構介紹2.2 &#x1f514;Linux …

WebFlux相關問題及答案(2024)

1、什么是Spring WebFlux&#xff1f; Spring WebFlux 是 Spring Framework 5.0 中引入的一個全新的反應式框架&#xff0c;用于構建異步、非阻塞且事件驅動的服務。它允許開發者使用響應式編程模型來處理并發性很高的操作&#xff0c;而無需擔心傳統的多線程環境中的復雜性。…

poi工具讀寫excel操作學習總結

寫在前面的話 POI作為比較早期的Excel處理工具&#xff0c;其使用較為成熟且廣泛。EasyExcel相較之下&#xff0c;則是相對較新的工具&#xff0c;其卻有著比POI更為優越的一些特性&#xff0c;如更加簡單的API接口和更加優秀的性能。 性能對比&#xff1a;在數據量較小的情況下…

mybatis mysql insert 主鍵id為空

錯誤示范 java代碼設置了param參數&#xff0c;但是sql 字段沒有帶上參數&#xff0c;例如 void insertV2(Param("historyDO") HistoryDO historyDO); <insert id"insertDuplicate" parameterType"com.test.entity.HistoryDO"keyProperty&…

MySQL:一行記錄如何

1、表空間文件結構 表空間由段「segment」、區「extent」、頁「page」、行「row」組成&#xff0c;InnoDB存儲引擎的邏輯存儲結構大致如下圖&#xff1a; 行 數據庫表中的記錄都是按「行」進行存放的&#xff0c;每行記錄根據不同的行格式&#xff0c;有不同的存儲結構。 頁…

hippy 調試demo運行聯調-mac環境準備篇

適用對于終端編譯環境不熟悉的人看&#xff0c;僅mac端 hippy 調試文檔官網地址 前提&#xff1a;請使用node16 聯調預覽效果圖&#xff1a; 編譯iOS Demo環境準備 未跑通&#xff0c;待補充 編譯Android Demo環境準備 1、正常安裝Android Studio 2、下載Android NDK&a…

Windows系統誤刪文件恢復

最近很多用戶反饋誤刪文件的場景比較多.下面華仔將講解數據恢復的原理和過程.以及一些注意事項。 建議的數據恢復軟件 1.EaseUS Data Recovery Wizard(易我數據恢復)需要斷網使用 2.Wondershare Recoverit(萬興數據恢復)&#xff0c; Windows系統刪除文件原理&#xff1a;如果是…

Android ShellUtils手機管理器

1. Android ShellUtils手機管理器 Android Shell工具類&#xff0c;可用于檢查系統root權限&#xff0c;并在shell或root用戶下執行shell命令。如&#xff1a; checkRootPermission() 檢查root權限 。execCommand(String[] commands, boolean isRoot, boolean isNeedResultMsg)…