【C++】學習筆記——多態_1

文章目錄

  • 十二、繼承
    • 8. 繼承和組合
  • 十三、多態
    • 1. 多態的概念
    • 2. 多態的定義和實現
      • 虛函數重寫的兩個特殊情況
      • override 和 ?nal
    • 3. 多態的原理
      • 1. 虛函數表
  • 未完待續


十二、繼承

8. 繼承和組合

我們已經知道了什么是繼承,那組合又是什么?下面這種情況就是 組合

class A
{//
};class B
{
private:A _a;
};

組合和繼承都是讓代碼復用,但是繼承的復用是一種 白箱復用 ,父類的內部細節是對子類透明的,根透明箱子一樣。而組合的復用是一種 黑箱復用 ,因為對象的內部細節是不可見的。
繼承一定程度破壞了父類的封裝,父類的改變,對子類有很大的影響。子類和父類間的依賴關系很強,耦合度高組合類之間沒有很強的依賴關系,耦合度低。優先使用對象組合有助于保持每個類被封裝

優先使用對象組合,而不是繼承。

public繼承是一種 is-a 的關系。也就是說每個子類對象都是一個父類對象。
組合是一種 has-a 的關系。假設B組合了A,每個B對象中都有一個A對象。

十三、多態

1. 多態的概念

多態 通俗來說,就是多種形態,具體點就是去完成某個行為,當不同的對象去完成某個行為時會產生出不同的狀態 。舉個栗子:比如買票這個行為,當普通人買票時,是全價買票;學生買票時,是半價買票;軍人買票時是優先買票。

2. 多態的定義和實現

我們先實現一下多態,來嘗嘗鮮:

#include<iostream>
using namespace std;class Person
{
public:virtual void BuyTicket(){cout << "買票-全價" << endl;}
};class Student : public Person
{
public:virtual void BuyTicket(){cout << "買票-半價" << endl;}
};// 多態
void Func(Person& p)
{p.BuyTicket();
}int main()
{Person ps;Student st;Func(ps);// 子類可以賦值給父類---切片Func(st);return 0;
}

在這里插入圖片描述
在繼承中想要構成多態是有條件的。

1. 必須通過父類的指針或者引用調用虛函數。
2. 被調用的函數必須是 虛函數 ,且子類必須對父類的虛函數進行重寫。

虛函數的重寫(覆蓋/隱藏):子類中有一個跟父類完全相同的虛函數(即子類虛函數與父類虛函數的 返回值類型、函數名字、參數列表完全相同),稱子類的虛函數重寫了父類的虛函數。(實際上父類的虛函數可以被子類繼承,所以只要父類寫上 virtual ,子類即使不寫 virtual 也能構成重寫)

關于重寫:重寫是重寫的 實現僅僅會改變實現方式,聲明并不會改變

虛函數重寫的兩個特殊情況

協變
在虛函數重寫時,父類和子類的虛函數返回類型可以不同,但要求返回類型必須是父子類關系的指針和引用,則稱為 協變

#include<iostream>
using namespace std;class A {};
class B : public A {};class Person
{
public:// 虛函數重寫,返回類型是對應的指針或引用virtual A* f(){cout << "A::f()" << endl;return new A;}
};class Student : public Person
{
public:// 虛函數重寫,返回類型是對應的指針或引用virtual B* f(){cout << "B::f()" << endl;return new B;}
};int main()
{Person* p = new Student;p->f();return 0;
}

在這里插入圖片描述
當返回類型是對應的指針或引用時成功實現多態,當返回類型不是時:

#include<iostream>
using namespace std;class A {};
class B : public A {};class Person
{
public:// 返回類型不同且不說相應的指針或引用virtual A f(){cout << "A::f()" << endl;return *new A;}
};class Student : public Person
{
public:// 返回類型不同且不說相應的指針或引用virtual B f(){cout << "B::f()" << endl;return *new B;}
};int main()
{Person* p = new Student;p->f();return 0;
}

在這里插入圖片描述
析構函數的重寫
如果父類的析構函數為虛函數,此時子類析構函數只要定義,無論是否加 virtual 關鍵字,都與父類的析構函數構成重寫。原因是編譯器對析構函數的名稱做了特殊處理,編譯后所以析構函數的名稱統一處理成 destructor

當父類的析構函數不是虛函數時,如下情況則會:

#include<iostream>
using namespace std;class Person
{
public:~Person(){cout << "~Person()" << endl;}
};class Student : public Person
{
public:~Student(){cout << "~Student()" << endl;}
};int main()
{// 父類指針指向父類對象Person* p1 = new Person;// 父類指針指向子類對象Person* p2 = new Student;delete p1;cout << endl;delete p2;return 0;
}

在這里插入圖片描述
沒能成功進行多態調用,訪問的還是父類的析構函數。當父類的析構函數是虛函數時:

#include<iostream>
using namespace std;class Person
{
public:virtual ~Person(){cout << "~Person()" << endl;}
};class Student : public Person
{
public:// 子類可以不寫 virtual ,自動構成虛函數重寫~Student(){cout << "~Student()" << endl;}
};
// 只有派生類Student的析構函數重寫了Person的析構函數
//下面的delete對象調用析構函數,才能構成多態
//才能保證p1和p2指向的對象正確的調用析構函數
int main()
{// 父類指針指向父類對象Person* p1 = new Person;// 父類指針指向子類對象Person* p2 = new Student;delete p1;cout << endl;delete p2;return 0;
}

在這里插入圖片描述
成功構成多態調用。我們怎么分辨 普通調用多態調用 呢?

普通調用?看指針或引用或者對象的類型
多態調用?看指針或引用指向的對象

在這里插入圖片描述

override 和 ?nal

如果我們想實現一個類,使其不能被繼承,應該怎么做?方法一:將父類的構造函數私有化,由于子類的構造函數必須調用父類的構造函數,所以父類的構造函數私有化會導致子類無法實例出對象。方法二:使用關鍵字 final

// 父類增加關鍵詞 final
class A final
{//
};class B : public A
{//
};

在這里插入圖片描述

?nal 還可以修飾虛函數,表示該虛函數不能再被重寫。

class Car
{
public:virtual void Drive() final{//}
};class Benz :public Car
{
public:virtual void Drive(){cout << "Benz-舒適" << endl;}
};

在這里插入圖片描述
override 可以檢查子類虛函數是否重寫了父類某個虛函數,如果沒有重寫則編譯報錯。

class Car
{
public:void Drive(){//}
};class Benz :public Car
{
public:// override 寫在子類后面virtual void Drive() override{cout << "Benz-舒適" << endl;}
};

在這里插入圖片描述

3. 多態的原理

1. 虛函數表

這里常考一道筆試題:sizeof(Base)是多少?

class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};int main()
{Base bb;cout << sizeof(Base) << endl;return 0;
}

在這里插入圖片描述
答案是:8;原因是,int 占 4 個字節,而只要類里面有虛函數,類就會在內部 額外生成一個指針 ,指針指向函數指針數組,函數指針數組里存的都是虛函數的地址,稱為 虛函數表 。指針占 4 個字節,故答案是 8 。
在這里插入圖片描述
對于上面的代碼,我們再進行改造一下:

#include<iostream>
using namespace std;class Base
{
public:// 虛函數virtual void Func1(){cout << "Base::Func1()" << endl;}// 虛函數virtual void Func2(){cout << "Base::Func2()" << endl;}// 普通函數void Func3(){cout << "Base::Func3()" << endl;}
private:int _b = 1;
};class Derive : public Base
{
public:// 虛函數重寫virtual void Func1(){cout << "Derive::Func1()" << endl;}
private:int _d = 2;
};int main()
{Base b;Derive d;return 0;
}

在這里插入圖片描述
我們發現,父類b對象和子類d對象虛函數表是不一樣的,這里我們發現Func1完成了重寫,所以d的虛函數表中存的是重寫的Derive::Func1,所以虛函數的重寫也叫作覆蓋,覆蓋就是指虛函數表中虛函數的覆蓋。b對象的虛函數表先拷貝一份父類的虛函數表,然后子類重寫的函數覆蓋進b對象的虛函數表。重寫是語法的叫法,覆蓋是原理層的叫法。Func3由于不是虛函數,所以沒有進入虛函數表。
運行時是通過本身的父類虛函數表或者切片的父類虛函數表(自己的)找到相應的虛函數,不同的對象虛函數表不同,因此實現多態。


未完待續

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

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

相關文章

英語學習筆記13——A new dress

A new dress 一件新連衣裙 詞匯 Vocabulary colour / color n. 顏色 v. 上色&#xff0c;涂色  英  美 顏色短語&#xff1a;green hand 新手      black tea 紅茶      white house 白宮      black sheep 害群之馬 英文顏色類詞匯&#xff1a; red 紅色…

鴻蒙開發接口Ability框架:【ApplicationContext】

ApplicationContext ApplicationContext模塊提供開發者應用級別的的上下文的能力&#xff0c;包括提供注冊及取消注冊應用內組件生命周期的監聽接口。 說明&#xff1a; 開發前請熟悉鴻蒙開發指導文檔&#xff1a; gitee.com/li-shizhen-skin/harmony-os/blob/master/README.m…

靜態IP代理:網絡世界的隱秘通道

在數字化時代&#xff0c;網絡安全和隱私保護日益受到重視。靜態IP代理作為一種網絡服務&#xff0c;為用戶提供了一個穩定且可預測的網絡連接方式&#xff0c;同時保護了用戶的在線身份。本文將從五個方面深入探討靜態IP代理的概念、優勢、應用場景、技術實現以及選擇時的考量…

C語言學習【printf函數和scanf函數】

C語言學習【printf函數和scanf函數】 printf()函數和scanf()函數可以讓用戶與程序交流&#xff0c;是輸入/輸出函數 printf()函數 請求printf()函數打印數據的指令要與待打印數據的類型相匹配。例如&#xff0c;打印整數時使用%d&#xff0c;打印字符時使用%c。這些符號被稱…

實戰項目技術點(1)

1、什么是REST&#xff1f; REST&#xff08;Representational State Transfer&#xff09;&#xff0c;表述性狀態轉換&#xff0c;它是一種軟件架構風格。傳統URL風格如下&#xff1a; http://localhost:8080/user/getById?id1 GET&#xff1a;查詢id為1的用戶 http://loc…

網絡常識一:常用工具

作為后端開發&#xff0c;特別是網絡游戲后端開發&#xff0c;經常需要處理各種現網問題&#xff0c;其中有不少是網絡相關的。 下面列舉的工具主要是在 linux 下的&#xff0c;診斷線上服務器問題的時候&#xff0c;往往是分秒必爭&#xff0c;所以這些工具都要用得很熟練&am…

程序在銀河麒麟系統下實現開機自啟及創建桌面快捷方式

目錄 1. 機器環境說明 2. 程序開機自啟動設置 2.桌面快捷方式設置 3. 附加說明 1. 機器環境說明 機器安裝的銀河麒麟操作系統屬性如下&#xff1a; 2. 程序開機自啟動設置 第1步&#xff1a;編寫一個腳本,用于自動化啟動&#xff0c;為便于后文描述&#xff0c;該腳本名稱…

干貨教程【軟件篇】| 免費實現游戲加速自由

需要這個游戲加速軟件的小伙伴可以關注一下文章底部公眾號&#xff0c;回復關鍵詞【zdjs】即可獲取。 該軟件可以實現免費的游戲加速&#xff0c;實測延遲低體驗好&#xff01; 建議看到后趕緊保存下來防止丟失&#xff01; 下面講一下該軟件安裝流程~ 通過鏈接可以得到下面…

2.go語言初始(二)

本篇博客涉及到go 的基礎數據類型、 go 語言中的運算符、轉義字符、格式化輸出、字符串操作 go 語言中的運算符 在 go 語言中&#xff0c;基本數據類型主要包括以下幾類&#xff1a;整數類型、浮點數類型、復數類型、布爾類型、字符串類型、字節類型&#xff08;byte&#xf…

Linux進程控制——Linux進程程序替換

前言&#xff1a;Linux進程控制包含了進程終止&#xff0c;進程等待&#xff0c;進程程序替換。走到現在我們也只剩下進程程序替換沒介紹了&#xff0c;那么讓我們來看看進程程序替換到底是什么&#xff01; 本篇主要內容&#xff1a; 替換原理 替換函數 實現簡易shell 我們所創…

【sql】sql中with as 介紹與使用jsqlparser解析sql

文章目錄 一. 定義二. 用法三. 解析 一. 定義 with A as (select * from class) 也就是將重復用到的大批量 的SQL語句&#xff0c;放到with as 中&#xff0c;加一個別名&#xff0c;在后面用到的時候就可以直接用。對于大批量的SQL數據&#xff0c;起到優化的作用。 with子句…

Broad Learning System (BLS) 寬度學習系統

寬度學習&#xff08;Broad Learning System, BLS&#xff09;是一種有效的神經網絡學習框架&#xff0c;旨在通過擴展網絡的寬度而不是深度來提高學習能力和效率。與傳統的深度學習相比&#xff0c;寬度學習通過堆疊多層特征節點和增強節點來構建網絡&#xff0c;從而避免了深…

oracle 臨時表

oracle 臨時表 conn scott/tiger --會話內保留行 drop table tmp1; CREATE GLOBAL TEMPORARY TABLE tmp1 ON COMMIT PRESERVE ROWS AS SELECT * FROM emp;SELECT TABLESPACE_NAME,CURRENT_USERS FROM V$SORT_SEGMENT;SELECT * FROM TMP1; INSERT INTO TMP1 SELECT * FROM…

Maven依賴管理項目構建

Maven依賴管理項目構建工具 目錄 文章目錄 Maven依賴管理項目構建工具目錄一、Maven簡介1、為什么學習Maven1.1、Maven是一個依賴管理工具1.2、Maven是一個構建工具1.3、結論 2. Maven介紹3. Maven軟件工作原理模型圖&#xff08;了解&#xff09; 二、Maven安裝和配置1. Mave…

PostgreSQL擴展之PGroonga:多語言全文搜索

簡介 PGroonga 是一個 PostgreSQL 擴展&#xff0c;它增加了基于 Groonga 的全文搜索索引方法。雖然原生的 PostgreSQL 支持全文索引&#xff0c;但它僅限于基于字母和數字的語言。PGroonga 提供了更廣泛的字符支持&#xff0c;使其成為 PostgreSQL 支持的語言的超集&#xff…

(實測驗證)Gitee代碼托管嘗試(一)——克隆/下載

一、登錄 Gitee&#xff08;碼云&#xff09;代碼托管平臺&#xff1a; Gitee - 基于 Git 的代碼托管和研發協作平臺 新建個人賬戶如下&#xff1a; 二、SSH 公鑰設置 1、在git安裝目錄打開“git-cmd.exe”; 2、通過命令 ssh-keygen 生成 SSH Key&#xff1a; ssh-keygen …

Linux進程概念總結

這里總結下Linux進程概念總結? 馮諾依曼&#xff1a; CPU 運算器與控制器RAM 內存&#xff08;存儲器&#xff09;Cache 緩存&#xff08;一種技術&#xff09;不屬于馮諾依曼體系結構。ROM 磁盤&#xff08;輸入輸出設備&#xff09;磁盤 既可以從硬盤讀取數據也可以向硬盤…

3-3 基于RYU的流量風暴事件原理與響應策略

在傳統網絡中&#xff0c;存在著一定的廣播流量&#xff0c;占據了一部分的網絡帶寬。同時&#xff0c;在有環的拓撲中&#xff0c;如果不運行某些協議&#xff0c;廣播數據還會引起網絡風暴&#xff0c;使網絡癱瘓。 如有以下的一個網絡拓撲結構&#xff08;3_2_topoplus.py) …

零基礎學Java第十四天之枚舉

枚舉 1、理解 枚舉&#xff08;Enumeration&#xff0c;通常簡寫為enum&#xff09;在編程語言中是一種特殊的數據類型&#xff0c;它允許你為變量定義一組命名的常量。這些常量通常表示一組有限的值&#xff0c;比如一周的七天、顏色的集合或者狀態的集合等。枚舉類型的主要目…

es 分詞器(五)之elasticsearch-analysis-jieba 8.7.0

es 分詞器&#xff08;五&#xff09;之elasticsearch-analysis-jieba 8.7.0 今天咱們就來講一下es jieba 8.7.0 分詞器的實現&#xff0c;以及8.x其它版本的實現方式&#xff0c;如果想直接使用es 結巴8.x版本&#xff0c;請直接修改pom文件的elasticsearch.version版本號即可…