網(wǎng)站首頁 編程語言 正文
多態(tài)基本概念和原理剖析
多態(tài)
:多態(tài)是C++面向?qū)ο蟮娜筇匦灾弧6鄳B(tài)分為靜態(tài)多態(tài)和動(dòng)態(tài)多態(tài)。靜態(tài)多態(tài)
:函數(shù)重載和運(yùn)算符重載屬于靜態(tài)多態(tài),復(fù)用函數(shù)名。動(dòng)態(tài)多態(tài)
:派生類和虛函數(shù)實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)。
區(qū)別:
靜態(tài)多態(tài)的函數(shù)地址早綁定,編譯階段確定函數(shù)地址。
動(dòng)態(tài)多態(tài)的函數(shù)地址晚綁定,運(yùn)行階段確定函數(shù)地址。
#include <iostream> using namespace std; //動(dòng)態(tài)多態(tài)滿足條件 //1、有繼承關(guān)系 //2、子類重寫父類的虛函數(shù) 重寫是指返回值 函數(shù)名 參數(shù)完全一樣 重載是指函數(shù)名相同 //動(dòng)態(tài)多態(tài)使用 //父類的指針或引用,指向子類對(duì)象 //當(dāng)子類重寫父類的虛函數(shù),子類中的虛函數(shù)表內(nèi)部會(huì)替換成子類的虛函數(shù)地址,將&Animal::speak替換成&Cat::speak //動(dòng)物類 class Animal { public: //虛函數(shù) virtual void speak(){ cout << "動(dòng)物在說話" << endl; } }; //貓類 class Cat :public Animal { public: void speak() { cout << "小貓?jiān)谡f話" << endl; } }; //狗類 class Dog :public Animal { public: void speak() { cout << "小狗在說話" << endl; } }; //執(zhí)行說話的函數(shù) //地址早綁定,在編譯階段就確定了函數(shù)的地址 //雖然想讓貓說話,但是這里輸出了動(dòng)物在說話 因?yàn)榈刂吩缃壎? //如果想讓貓說話,這個(gè)函數(shù)的地址就不能提前綁定,需要在運(yùn)行階段進(jìn)行綁定,也即地址晚綁定 void DoSpeak(Animal &animal) { animal.speak(); } void test01() { Cat cat; DoSpeak(cat); //雖然想讓貓說話,但是這里輸出了動(dòng)物在說話,因?yàn)榈刂吩缃壎ǎ舾割惛臑樘摵瘮?shù)地址晚綁定,就是貓?jiān)谡f話 Dog dog; DoSpeak(dog); } void test02() { cout << "size of Animal = " << sizeof(Animal) << endl; } int main() { //test01(); test02(); system("pause"); return 0; }
多態(tài)案例1 計(jì)算器類
分別使用普通寫法和多態(tài)技術(shù),設(shè)計(jì)實(shí)現(xiàn)兩個(gè)操作數(shù)進(jìn)行運(yùn)算的計(jì)算器類。
多態(tài)優(yōu)點(diǎn)
:
1、代碼組織結(jié)構(gòu)清晰
2、可讀性強(qiáng)
3、便于前期和后期的擴(kuò)展與維護(hù)
#include<iostream> using namespace std; //普通寫法 class Calculate { public: int GetResult(string oper) { if (oper == "+") { return num1 + num2; } else if (oper == "-") { return num1 - num2; } else if (oper == "*") { return num1 * num2; } //如果想擴(kuò)展新的功能,需要修改原碼,在實(shí)際開發(fā)中,提倡開閉原則:對(duì)擴(kuò)展進(jìn)行開發(fā),對(duì)修改進(jìn)行關(guān)閉 } int num1; int num2; }; void test01() { //創(chuàng)建計(jì)算器對(duì)象 Calculate c; c.num1 = 10; c.num2 = 10; cout << c.num1 << " + "<< c.num2 << " = " <<c.GetResult("+") << endl; cout << c.num1 << " - "<< c.num2 << " = " <<c.GetResult("-") << endl; cout << c.num1 << " * "<< c.num2 << " = " <<c.GetResult("*") << endl; } //利用多態(tài)實(shí)現(xiàn)計(jì)算器 //多態(tài)的優(yōu)點(diǎn) //1、組織結(jié)構(gòu)清晰 //2、可讀性強(qiáng) //3、對(duì)于前期擴(kuò)展和后期維護(hù)性能高 //實(shí)現(xiàn)計(jì)算器抽象類 class AbstractCalculate { public: virtual int Result() { return 0; } int m_num1; int m_num2; }; //加法計(jì)算器類 class AddCalculate : public AbstractCalculate{ public: int Result() { return m_num1 + m_num2; } }; //減法計(jì)算器類 class SubCalculate : public AbstractCalculate { public: int Result() { return m_num1 - m_num2; } }; //乘法計(jì)算器類 class MulCalculate : public AbstractCalculate { public: int Result() { return m_num1 * m_num2; } }; void test02() { //多態(tài)使用指針 //父類指針或者引用指向子類對(duì)象 //加法運(yùn)算 AbstractCalculate* p = new AddCalculate; p->m_num1 = 100; p->m_num2 = 100; cout << p->m_num1 << " + " << p->m_num2 << " = " << p->Result() <<endl; //用完后釋放新開辟的堆區(qū)數(shù)據(jù) delete p; //減法運(yùn)算 p = new SubCalculate; p->m_num1 = 100; p->m_num2 = 100; cout << p->m_num1 << " - " << p->m_num2 << " = " << p->Result() << endl; delete p; //乘法運(yùn)算 p = new MulCalculate; p->m_num1 = 100; p->m_num2 = 100; cout << p->m_num1 << " * " << p->m_num2 << " = " << p->Result() << endl; delete p; } int main() { //test01(); test02(); system("pause"); return 0; }
純虛函數(shù)和抽象類
在多態(tài)中,通常父類函數(shù)的虛函數(shù)是無意義的,主要都是調(diào)用子類重寫的內(nèi)容。
因此可以將虛函數(shù)改為純虛函數(shù)。
當(dāng)類中有了純虛函數(shù),這個(gè)類也稱為抽象類。
.抽象類特點(diǎn)
:無法實(shí)例化對(duì)象;子類必須重新抽象類中的純虛函數(shù),否則也屬于抽象類。
#include<iostream> using namespace std; //純虛函數(shù)語法 virtual 返回值 函數(shù)名(參數(shù)列表) = 0; //當(dāng)類中有了純虛函數(shù),這個(gè)類也稱為抽象類,抽象類不能實(shí)例化對(duì)象,子類必須重寫抽象類里的純虛函數(shù),不然子類也屬于抽象類 class Base { public: //純虛函數(shù) 只要有純虛函數(shù)這個(gè)類就是抽象類 virtual void func() = 0; }; class Son :public Base { public: void func() { cout << "Son類下的func函數(shù)調(diào)用" << endl; } }; void test01() { /*抽象類是無法實(shí)例化對(duì)象的 Base b; new Base;*/ Son s; s.func(); //多態(tài)方式調(diào)用 父類的指針new一個(gè)子類 Base* p = new Son; p->func(); delete p; } int main() { test01(); system("pause"); return 0; }
多態(tài)案例2 制作飲品
利用多態(tài)技術(shù)實(shí)現(xiàn)制作飲品的流程,提供抽象制作飲品基類,提供子類制作咖啡和茶。
這里使用指針多態(tài)而不是引用的原因在于,引用需先實(shí)例化對(duì)象,但是有純虛函數(shù)的抽象類無法實(shí)例化對(duì)象。
#include<iostream> using namespace std; class AbstractDrinking { public: //煮水 virtual void Boil() = 0; //沖泡 virtual void Brew() = 0; //倒入杯中 virtual void DropInCup() = 0; //加入輔料 virtual void AddSomething() = 0; //制作飲品 void MakeDrink() { Boil(); Brew(); DropInCup(); AddSomething(); } }; //制作咖啡流程 class Coffee : public AbstractDrinking { public: //煮水 virtual void Boil() { cout << "煮一壺沸水" << endl; } //沖泡 virtual void Brew() { cout << "沖泡咖啡粉" << endl; } //倒入杯中 virtual void DropInCup() { cout << "將咖啡倒入杯中" << endl; } //加入輔料 virtual void AddSomething() { cout << "將牛奶和白糖加入咖啡中" << endl; } }; //制作茶水流程 class Tea : public AbstractDrinking { public: //煮水 virtual void Boil() { cout << "煮一壺沸水" << endl; } //沖泡 virtual void Brew() { cout << "沖泡西湖龍井" << endl; } //倒入杯中 virtual void DropInCup() { cout << "將茶水倒入杯中" << endl; } //加入輔料 virtual void AddSomething() { cout << "將枸杞加入茶水中" << endl; } }; //制作飲品函數(shù) void DoWork(AbstractDrinking *p) { //這里相當(dāng)于 AbstractDrinking *p = new Coffee; //若這里不使用指針,改為引用,test01的函數(shù)就不能使用 //因?yàn)橐檬窍葘?shí)例化對(duì)象再調(diào)用對(duì)象,抽象類無法實(shí)例化對(duì)象 p->MakeDrink(); delete p; //防止內(nèi)存泄露,使用后記得釋放內(nèi)存 } void test01() { //想喝一杯咖啡 DoWork(new Coffee); cout << "-----------------------------------" << endl; //想喝一杯西湖龍井茶 DoWork(new Tea); } int main() { test01(); system("pause"); return 0; }
虛析構(gòu)和純虛析構(gòu)
使用多態(tài)技術(shù)時(shí),如果有子類開辟到堆區(qū),那么父類指針在釋放時(shí)無法調(diào)用到子類的析構(gòu)代碼。解決該問題的方法是
將父類中的析構(gòu)函數(shù)改為虛析構(gòu)或者純虛析構(gòu)
。
虛析構(gòu)和純虛析構(gòu)共性
:
1、可以解決父類指針釋放子類對(duì)象
2、都需要有具體的函數(shù)實(shí)現(xiàn)虛析構(gòu)和純虛析構(gòu)區(qū)別
:
如果是純虛析構(gòu),該類屬于抽象類,無法實(shí)例化對(duì)象。
#include<iostream> #include<string> using namespace std; //動(dòng)物類 class Animal { public: Animal() { cout << "這是Animal的構(gòu)造函數(shù)調(diào)用" << endl; } //純虛函數(shù) virtual void Speak() = 0; //為了走子類的虛構(gòu)函數(shù),需要將父類的虛構(gòu)函數(shù)改為虛析構(gòu) /*virtual ~Animal() { cout << "這是Animal的析構(gòu)函數(shù)調(diào)用" << endl; }*/ //純虛析構(gòu)函數(shù) 父類的堆區(qū)可能也被開辟需要釋放,因此父類的析構(gòu)也必須需要聲明,也需要實(shí)現(xiàn) //有純虛析構(gòu)后這個(gè)類也屬于抽象類,無法實(shí)例化對(duì)象 virtual ~Animal() = 0; }; Animal:: ~Animal() { cout << "這是Animal類的寫在外部的純虛析構(gòu)函數(shù)調(diào)用" << endl; } //貓類 class Cat : public Animal{ public: //Cat類的構(gòu)造函數(shù) Cat(string name) { cout << "這是Cat類的構(gòu)造函數(shù)" << endl; m_name = new string(name); } void Speak() { cout << *m_name<<"小貓的叫聲" << endl; } ~Cat() { if (m_name != NULL) { //父類指針在析構(gòu)的時(shí)候,不會(huì)調(diào)用子類中的析構(gòu)函數(shù),導(dǎo)致子類中有堆區(qū)開辟的數(shù)據(jù)沒有被釋放干凈導(dǎo)致內(nèi)存泄露 cout << "這是Cat類的析構(gòu)函數(shù)" << endl; delete m_name; m_name = NULL; } } string *m_name; }; void test01() { Animal* p = new Cat("Tom"); //如果子類中沒有堆區(qū)數(shù)據(jù),可以不寫虛析構(gòu)或純虛析構(gòu)函數(shù) p->Speak(); delete p; //堆區(qū)開辟以后,釋放指針 } int main() { test01(); system("pause"); return 0; }
多態(tài)案例3 電腦組裝
電腦主要部件為CPU,顯卡和內(nèi)存條。將每個(gè)零件封裝出抽象基類,并提供不同的廠商生產(chǎn)不同的零件。創(chuàng)建電腦類提供讓電腦工作的函數(shù),并且調(diào)用每個(gè)零件工作的接口。測(cè)試時(shí)組裝三臺(tái)不同的電腦進(jìn)行工作。
#include<iostream> using namespace std; //每個(gè)零件都封裝出抽象的基類 //CPU類 class Cpu { public: //抽象的計(jì)算函數(shù) virtual void Calculate() = 0; //virtual ~Cpu() = 0; }; //Cpu::~Cpu() { // cout << "Cpu類寫在外部的純虛析構(gòu)函數(shù)調(diào)用" << endl; //} //顯卡類 class VideoCard { public: //抽象的顯示函數(shù) virtual void Display() = 0; }; //內(nèi)存條類 class Memory { public: //抽象的存儲(chǔ)函數(shù) virtual void Storage() = 0; }; //電腦類 class Computer { public: Computer(Cpu* cpu, VideoCard* videocard, Memory* memory) { m_cpu = cpu; m_videocard = videocard; m_memory = memory; } //工作函數(shù):讓零件調(diào)用起來的工作接口 父類指針調(diào)用接口時(shí)就已經(jīng)是多態(tài)技術(shù)了 void DoWork() { //Cpu進(jìn)行計(jì)算操作 m_cpu->Calculate(); //顯卡進(jìn)行顯示操作 m_videocard->Display(); //內(nèi)存條進(jìn)行存儲(chǔ)操作 m_memory->Storage(); } //提供析構(gòu)函數(shù)釋放電腦的三個(gè)零件 ~Computer() { if (m_cpu != NULL) { delete m_cpu; m_cpu = NULL; } if (m_videocard != NULL) { delete m_videocard; m_videocard = NULL; } if (m_memory != NULL) { delete m_memory; m_memory = NULL; } } private: Cpu *m_cpu; VideoCard *m_videocard; Memory *m_memory; }; //具體廠商/// //因特爾廠商 class IntelCpu :public Cpu{ public: //子類重寫父類的虛函數(shù) virtual void Calculate() { cout << "Intel的Cpu開始計(jì)算了!" << endl; } /*~IntelCpu() { cout << "這是子類IntelCpu的析構(gòu)函數(shù)調(diào)用" << endl; }*/ }; class IntelVideoCard :public VideoCard { public: //子類重寫父類的虛函數(shù) virtual void Display() { cout << "Intel的顯卡開始顯示屏幕了!" << endl; } }; class IntelMemory :public Memory { public: //子類重寫父類的虛函數(shù) virtual void Storage() { cout << "Intel的內(nèi)存條開始存儲(chǔ)數(shù)據(jù)了!" << endl; } }; //Lenovo廠商 class LenovoCpu :public Cpu { public: //子類重寫父類的虛函數(shù) virtual void Calculate() { cout << "Lenovo的Cpu開始計(jì)算了!" << endl; } }; class LenovoVideoCard :public VideoCard { public: //子類重寫父類的虛函數(shù) virtual void Display() { cout << "Lenovo的顯卡開始顯示屏幕了!" << endl; } }; class LenovoMemory :public Memory { public: //子類重寫父類的虛函數(shù) virtual void Storage() { cout << "Lenovo的內(nèi)存條開始存儲(chǔ)數(shù)據(jù)了!" << endl; } }; //開始測(cè)試,組裝不同的電腦 void test01() { //第一臺(tái)電腦的零件 //父類指針指向了子類對(duì)象,利用了多態(tài)技術(shù) Cpu* intelcpu = new IntelCpu; VideoCard* intelvideocard = new IntelVideoCard; Memory* intelmemory = new IntelMemory; //創(chuàng)建第一臺(tái)電腦 cout << "組裝好的第一臺(tái)電腦配置如下:" << endl; Computer* computer1 = new Computer(intelcpu,intelvideocard,intelmemory); computer1->DoWork(); delete computer1; //創(chuàng)建第二臺(tái)電腦 cout << "組裝好的第二臺(tái)電腦配置如下:" << endl; Computer* computer2 = new Computer(new LenovoCpu, new LenovoVideoCard, new LenovoMemory); computer2->DoWork(); delete computer2; //創(chuàng)建第三臺(tái)電腦 cout << "組裝好的第三臺(tái)電腦配置如下:" << endl; Computer* computer3 = new Computer(new LenovoCpu, new IntelVideoCard, new LenovoMemory); computer3->DoWork(); delete computer3; } int main() { test01(); system("pause"); return 0; }
原文鏈接:https://blog.csdn.net/weixin_44884304/article/details/122066349
相關(guān)推薦
- 2024-03-15 Gitea Webhook報(bào)錯(cuò) webhook.ALLOWED_HOST_LIST setting
- 2023-04-18 spark?dataframe全局排序id與分組后保留最大值行_python
- 2023-05-13 python中數(shù)字列表轉(zhuǎn)化為數(shù)字字符串的實(shí)例代碼_python
- 2023-05-15 sql語句中臨時(shí)表使用實(shí)例詳解_MsSql
- 2024-03-24 SpringBoot工具庫:解決SpringBoot2.*版本跨域問題
- 2022-03-16 使用Nginx實(shí)現(xiàn)端口轉(zhuǎn)發(fā)TCP代理的實(shí)現(xiàn)示例_nginx
- 2022-08-30 AndroidStudio編譯報(bào)錯(cuò) Connect to repo.maven.apache.org
- 2022-03-14 easypoi導(dǎo)入校驗(yàn)跳過空行_EasyPoi導(dǎo)入驗(yàn)證功能
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支