第十一章 多態

? ? ? ? 多態是面向對象開發過程中一個非常重要的概念。

11.1 多態概述

11.1.1 什么是多態

? ? ? ? 多態(polymorphism),從字面理解是“多種形態,多種形式”,是一種將不同的特殊行為泛化為當個特殊記號的機制。

多態從實現的角度可劃分為兩類:

(1)編譯時的多態:編譯過程中確定了同名操作的具體操作對象

(2)運行時的多態:程序運行時才動態確定操作所針對的具體對象

11.1.2? 多態的引入

先嘗試理解以下程序:

#include<iostream>
using namespace std;class Animal
{public:void sleep(){cout<<"Animal sleep"<<endl;}void breathe(){cout<<"Animal breath"<<endl;}
};
class Fish:public Animal
{public:void breathe(){cout<<"Fish double"<<endl;}
};
int main()
{Fish fh;Animal *an=&fh;an->breathe();return 0;
}

運行結果:

分析過程如下:

如何解決這個問題呢?可以通過多態來解決。

11.1.3 聯編

聯編是確定操作的具體對象的過程,只計算機程序自身彼此關聯的過程,即把一個標識符名和一個存儲地址聯系在一起的過程

根據進行階段的不同,可以分為靜態聯編與動態聯編,這兩種聯編過程分別對應多態的兩種實現方式,如下圖:

11.2 函數重載

? ? ? ? 函數重載與運算符重載可以實現編譯時的多態性。以下只介紹函數重載,運算符重載見第12章。

? ? ? ? 函數重載也稱為多態函數

(1)作用:使程序能夠用同一個名字來訪問一組相關的函數,提高程序靈活性。

(2)含義:函數名相同,但函數所帶的參數個數或數據類型不同(編譯系統會根據參數來決定調用哪個同名函數)????????

? ? ? ? 函數重載表現為兩種情況:

(1)參數個數或類型有所差別的重載

(2)函數的參數完全相同但屬于不同類

第一種情況與構造函數重載類似,不作追訴,只介紹第二種情況。當函數的參數完全相同但屬于不同類時,為了讓編譯能正確區分調用哪個類的同名函數,可用以下兩種方法別:

(1)用對象名區別,在函數名前加上對象名來限制對象名.函數名

(2)用類名和作用域運算符區別,在函數名前加“類名::”來限制(類名::函數名

示例代碼:

#include<iostream>
using namespace std;class Point
{int x,y;public:Point(int a,int b){x=a;y=b;}double area(){return x*y;}};
class Circle:public Point
{int r;public:Circle(int a,int b,int c):Point(a,b){r=c;}double area(){return 3.1416*r*r;}
};
int main()
{Point pob(15,15);Circle cob(20,20,10);cout<<"pob.area()="<<pob.area()<<endl<<endl;cout<<"cob.area()"<<cob.area()<<endl<<endl;cout<<"cob.Point::area()"<<cob.Point::area()<<endl<<endl;return 0;
}

運行結果:

通過函數重載方式,可以是同名函數實現不同功能,即實現了靜態多態性

11.3 虛函數

? ? ? ? ?虛函數是實現運行時多態的一個重要方式,是重載的另一種形式。也就是動態編聯。

11.3.1 定義虛函數

? ? ? ? 虛函數的定義是在基類中進行的,即把基類中需要定義為虛函數的成員函數聲明為virtual。虛函數定義的一般形式如下:

virtual <函數類型><函數名>(參數表)
{函數體;
}
virtual void breathe()
{cout<<"Animal breathe"<<endl;
}

? ? ? ? 當基類中的某個成員函數被聲明為虛函數后,就可以在派生類中重新定義。在派生類中重新定義時,其函數原型包括返回類型、函數名、參數個數和類型,參數的順序都必須與基類中的原型完全一致

示例代碼:(與第一個代碼做對比)

#include<iostream>
using namespace std;class Animal
{public:void sleep(){cout<<"Animal sleep"<<endl;}virtual void breathe(){cout<<"Animal breath"<<endl;}
};
class Fish:public Animal
{public:void breathe(){cout<<"Fish double"<<endl;}
};
int main()
{Fish fh;Animal *an=&fh;an->breathe();return 0;
}

運行結果:

代碼將基類中的成員函數breathe()定義為虛函數,即加上virtual關鍵字,然后在主函數main()中定義Animal對象指針指向Fish的對象fh,調用breathe()函數后,得到的結果就是預期輸出Fish bubble的結果。具體分析過程如下:

在使用派生類對象指針時應該注意:

(1)不能指向私有派生類的對象;

(2)指向公有派生類對象時,只能訪問派生類中從基類繼承下來的成員,不能直接訪問公有派生類中定義的成員。

(3)指向派生類對象的指針不能指向基類的對象;

虛函數可以很好的實現多態,在使用虛函數是應注意的問題如下:

(1)虛函數的聲明只能出現在類函數原型的聲明中,不能出現在函數體實現時;

(2)基類中只有保護成員或公有成員才能被聲明為虛函數

(3)在派生類中重新定義虛函數時,關鍵字virtual可以寫也可以不寫,但在容易引起混亂時,應該寫上關鍵字;

(4)動態編聯只能通過成員函數來調用或通過指針、引用來訪問虛函數

在派生類中程序定義基類中的虛函數,是函數重載的另一種形式,但它與函數重載又有區別:

(1)一般函數重載,要求其函數的參數數量或參數類型必須有所不同,函數的返回值也可以不同;

(2)重載一個虛函數時,要求函數名、返回類型、參數個數、參數的類型和參數的順序必須與基類中的虛函數的原型完全相同

11.3.2 多級繼承和虛函數

? ? ? ? 多級繼承可以看作是多個單繼承的組合,多級繼承的虛函數與單繼承的虛函數的調用相同。即不同類創建的對象調用的函數是不一樣的。

示例代碼:

#include<iostream>
using namespace std;class Base
{public:virtual void func(){cout<<"Base output"<<endl;}};
class Derived1:public Base
{public:void func(){cout<<"Derived1 output"<<endl;}
};
class Derived2:public Derived1
{public:void func(){cout<<"Derived2 output"<<endl;}
};
void test(Base &b)
{b.func();
}int main()
{Base bObj;Derived1 d1Obj;Derived2 d2Obj;test(bObj);cout<<endl;test(d1Obj);cout<<endl;test(d2Obj);cout<<endl;return 0;
}

運行結果:

????????上述代碼中定義了一個多級繼承,在基類中定義了虛函數func(),在主函數main()中調用該函數時,不同類創建的對象調用的函數是不一樣的,即實現了多態的“一個接口,多種實現”。

11.4 純虛函數與抽象類

? ? ? ? 抽象類是一種包含純虛函數的特殊類。建立抽象類時為了多態地使用抽象類的成員函數。

11.4.1 純虛函數

? ? ? ? 在基類中不能為虛函數給出一個有意義的實現時,可以將其聲明為純虛函數。純虛函數的實現可以留給派生類來完成。純虛函數的作用是為派生類提供一個一致的接口。一般來說,一個抽象類帶有至少一個純虛函數。純虛函數的一般定義如下:

? ? ? ? 純虛函數與普通函數定義的不同在于書寫形式加了“=0”,說明在基類中不用定義改函數的函數體。

示例代碼:純虛函數的函數體由派生類定義;

#include<iostream>
using namespace std;class Point
{protected:int x0,y0;public:Point(int i=0,int j=0){x0=i;y0=j;                }    virtual void set()=0;//聲明純虛函數
};
class Line:public Point
{protected:int x1,y1;public:Line(int i=0,int j=0,int m=0,int n=0):Point(i,j){x1=m;y1=j;}void set()//定義接口函數{cout<<"Line::set() called \n";}
};void setobj(Point *p)
{p->set();
}int main()
{Line *lineobj = new Line;setobj(lineobj);return 0;
}

運行結果:

11.4.2 抽象類

? ? ? ? 抽象類是包含純虛函數的一種特殊類,是為了抽象和設計而建立的,處于繼承層次結果的教上層。抽象類是不能建立對象的,為了強調一個類是抽象類,可講該類的構造函數聲明為保護的訪問控制權限。抽象類的主要作用如下:

(1)將有關的類組織在一個繼承層層次結構中,由抽象類來為它們提供一個公共的根,相關的子類是從這個根派生出來的。

(2)抽象類只描述這組子類溝通的操作接口,而完整的實現留給子類

使用抽象類是應注意:

(1)抽象類只能用做其他類的基類,不能創建抽象類的對象

(2)抽象類不能用做參數類型、函數的返回類型或顯示轉換的類型

(3)可以聲明抽象類的對象指針或對象引用,從而可以訪問派生類對象成員,實現多態編聯

(4)若派生類中沒有給出抽象類的所有純虛函數的函數體,派生類仍然是一個抽象類,若派生類中給出抽象類的所有純虛函數的函數體,則這個派生類不再是抽象類,可以創建自己的對象。

示例代碼:

#include<iostream>
using namespace std;class Vehicle
{protected:float speed;int total;public:Vehicle(float speed,int total){Vehicle::speed=speed;Vehicle::total=total;}virtual void ShowMember()=0;
};
class Car:public Vehicle
{protected:int aird;public:Car(int aird,float speed,int total):Vehicle(speed,total){Car::aird=aird;}void ShowMember(){cout<<"the speed is:"<<speed<<endl;cout<<"the total is:"<<total<<endl;cout<<"the aird is:"<<aird<<endl;}
};int main()
{//Vehicle a(100,4);Car b(250,150,4);b.ShowMember();return 0;
}

運行結果:

聲明抽象類Vehicle的對象a(100,4)時報錯,不能為抽象類聲明對象。

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

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

相關文章

RNN——循環神經網絡

一.基本結構 1.目標&#xff1a;處理序列數據&#xff08;時間序列&#xff0c;文本&#xff0c;語音等&#xff09;&#xff0c;捕捉時間維度上的依賴關系 核心機制&#xff1a;通過隱藏狀態&#xff08;hidden State&#xff09;傳遞歷史信息&#xff0c;每個時間步的輸入包…

性能提升手段--池化技術

看到hadoop代碼里有ByteBufferPool,使用池子來避免頻繁創建、銷毀ByteBuffer,減輕GC壓力,提高性能。 順便總結一下池化技術 一、什么是池化技術??? ??池化(Pooling)?? 是一種資源管理策略,通過??預先創建并復用資源??(如數據庫連接、線程、內存對象等)來提…

數據安全和合規性市場分析

一、什么是數據安全和合規性 在數據安全和合規性方面&#xff0c;存在著一系列重要的法律、法規和行業標準&#xff0c;這些規定了組織如何收集、存儲、處理和保護個人數據及其他敏感信息。企業之所以要遵守這些規定&#xff0c;是出于多方面的考量&#xff0c;既有法律責任&a…

【每日八股】復習計算機網絡 Day4:TCP 協議的其他相關問題

文章目錄 昨日內容復習已經建立了 TCP 連接&#xff0c;客戶端突然出現故障怎么辦&#xff1f;什么時候用長連接&#xff1f;短連接&#xff1f;TCP 的半連接隊列與全連接隊列&#xff1f;什么是 SYN 攻擊&#xff1f;如何避免&#xff1f;TIME_WAIT 的作用&#xff1f;過多如何…

React:<></>的存在是為了什么

1. <></> 是什么&#xff1f; <></> 是 React 的Fragment&#xff08;片段&#xff09;語法糖&#xff0c;等價于 <React.Fragment></React.Fragment>。 2. 它的作用 主要作用&#xff1a; 允許你在組件里返回多個元素&#xff0c;而不需…

cron定時任務

cron定時任務 一、Cron表達式的定義 基礎結構 Cron表達式是由空格分隔的6或7個字段組成的字符串&#xff0c;格式為&#xff1a; 秒 分 時 日 月 星期 [年]其中&#xff0c;年通常可以被省略 字段說明&#xff1a; 秒&#xff08;0-59&#xff09; 秒字段表示每分鐘的哪一…

分布式之易混淆概念

昨天寫UE寫的破防了&#xff0c;忘了寫文章&#xff0c;今天補一下分布式的一些概念。&#x1f61a; 在軟件架構領域&#xff0c;微服務、領域驅動設計&#xff08;DDD&#xff09;和分布式系統是三個高頻且容易被混淆的概念。許多開發者誤以為它們是“同一件事的不同說法”&a…

量子躍遷:Vue組件安全工程的基因重組與生態免疫(完全體終局篇)

開篇數字免疫系統的范式革命 在2025年某國際金融峰會期間&#xff0c;黑客組織利用量子計算技術對全球37個交易系統發起協同攻擊。傳統安全組件在2.7秒內集體失效&#xff0c;造成每秒超18億美元的交易漏洞。這場數字"切爾諾貝利"事件促使我們重新定義前端安全——組…

Operating System 實驗七 Linux文件系統實驗

實驗目標: 使用dd命令創建磁盤鏡像文件ext2.img并格式化為ext2文件系統,然后通過mount命令掛載到Linux主機文件系統。查看ext2文件系統的超級塊的信息,以及數據塊的數量、數據塊的大小、inode個數、空閑數據塊的數量等信息 在文件系統中創建文件xxxxx.txt(其中xxxxx為你的學…

模型識別能力錘煉及清單

大腦將注意力分配給需要消耗腦力的活動&#xff0c;通過學習技能&#xff0c;大腦也能更輕松的工作。這個時候&#xff0c;大腦負責管理注意力控制和努力控制的區域活動會大幅減少。沉浸式學習是學習一門新的語言的最佳方式&#xff0c;也是深入洞察錯綜復雜商業環境的絕佳途徑…

Android 混合開發實戰:統一 View 與 Compose 的淺色/深色主題方案

整個應用&#xff08;包括 View 和 Compose 部分&#xff09;的淺色/深色模式保持一致。以下是完整的解決方案&#xff1a; 全局配置方案 1. 基礎主題設置 在 res/values/themes.xml 和 res/values-night/themes.xml 中定義統一的主題&#xff1a; <!-- values/themes.x…

QT開發技術【QT實現桌面右下角消息】

一、效果 ![ 二、彈窗主體部分 noticewidget /* ** File name: NoticeWidget.h ** Author: ** Date: 2025-04-25 ** Brief: 通知欄控件 ** Copyright (C) 1392019713qq.com All rights reserved. */#include "../Include/NoticeWidget.h"…

在LiveGBS GB28181互聯網安防監控平臺中關于redis版本切換的方法說明

目錄 1、Redis服務2、如何切換REDIS? 2.1、停止啟動REDIS2.2、配置信令服務2.3、配置流媒體服務2.4、啟動3、搭建GB28181視頻直播平臺 1、Redis服務 在LivGBS中Redis作為數據交換、數據訂閱、數據發布的高速緩存服務。默認LiveCMS解壓目錄下會攜帶一個REDIS服務。如果已經有自…

vue3中的effectScope有什么作用,如何使用?如何自動清理

vue3中的effectScope有什么作用&#xff0c;如何使用?如何自動清理 vue3中的effectScope有什么作用&#xff0c;如何使用 官網介紹&#xff1a;作用特點簡單示例&#xff1a;自動清理示例 官網介紹&#xff1a; 創建一個 effect 作用域&#xff0c;可以捕獲其中所創建的響應…

搭建基于火災風險預測與防范的消防安全科普小程序

基于微信小程序的消防安全科普互動平臺的設計與實現&#xff0c;是關于微信小程序的&#xff0c;知識課程學習&#xff0c;包括學習后答題。 技術棧主要采用微信小程序云開發&#xff0c;有下面的模塊&#xff1a; 1.課程學習模塊 2.資訊模塊 3.答題模塊 4.我的模塊 還需…

python 與Redis操作整理

以下是使用 Python 操作 Redis 的完整整理&#xff0c;涵蓋基礎操作、高級功能及最佳實踐&#xff1a; 1. 安裝與連接 (1) 安裝庫 pip install redis(2) 基礎連接 import redis# 創建連接池&#xff08;推薦復用連接&#xff09; pool redis.ConnectionPool(hostlocalhost, …

什么時候使用Python 虛擬環境(venv)而不用conda

是的&#xff01;python3.9 -m venv rtdetr_env 是 Python 原生的虛擬環境&#xff08;venv&#xff09;&#xff0c;而 conda 是另一個流行的虛擬環境管理工具&#xff08;來自 Anaconda/Miniconda&#xff09;。下面我會詳細對比兩者的區別&#xff0c;并講解 venv 的基本用法…

ubuntu20.04安裝x11vnc遠程桌面

x11vnc是一個VNC服務器, 安裝后我們可以不依賴外部的顯示設備, 通過網絡遠程登錄ubuntu桌面。 安裝x11vnc sudo apt-get install x11vnc 設置VNC登錄密碼 sudo x11vnc -storepasswd /etc/x11vnc.pwd 設置x11vnc在開機時自動啟動 新建如下文件: sudo vi /lib/systemd/sys…

Maven的概念與初識Maven

目錄 一、Maven的概念 1. 什么是Maven 2. 項目構建&#xff1a;從代碼到部署的標準化流程 2.1 Maven構建生命周期 2.2 傳統構建 vs Maven構建 3. 依賴管理&#xff1a;解決“JAR地獄”的利器 3.1 依賴聲明 3.2 依賴傳遞與沖突解決 4. Maven倉庫&#xff1a;依賴的存儲…

Unity-Shader詳解-其二

前向渲染和延遲渲染 前向渲染和延遲渲染總的來說是我們的兩種主要的渲染方式。 我們在Unity的Project Settings中的Graphic界面能夠找到渲染隊列的設定&#xff1a; 我們也可以在Main Camera這里進行設置&#xff1a; 那這里我們首先介紹一下兩種渲染&#xff08;Forward R…