日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++(23)--多态性与虚函数

發布時間:2023/12/13 c/c++ 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++(23)--多态性与虚函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

多態性與虛函數

  • 1.靜態多態-重載
  • 2.動態多態-重寫
    • 2.1 向上轉換/向下轉換
  • 3.虛函數的工作原理
  • 4.純虛函數和抽象類
  • 5.補充項目(都市浮生記)-卒

《老九學堂C++課程》學習筆記?!独暇艑W堂C++課程》詳情請到B站搜索《老九零基礎學編程C++入門》
-------------簡單的事情重復做,重復的事情用心做,用心的事情堅持做(老九君)---------------

多態–多種表現形式,生物學名詞。
同一個名稱的函數,可以實現不同的功能。

什么是多態
面向對象編程的多態性包括:
1.面向不同的對象發送同一條信息–多個對象調用同一個函數
2.不同的對象在接收時回產生不同的行為–
不同的行為–不同的實現,即執行不同的函數功能。函數名相同,但執行的具體細節不同。

1.靜態多態-重載

靜態多態–重載
靜態多態也叫編譯時多態。
demo1.游戲引擎調用得中類對象進行移動操作

// GameCore.h // // Created by 陳瑩瑩 on 2021/3/24. // #ifndef CHAPTER14_GAMECORE_H #define CHAPTER14_GAMECORE_H #include <iostream> #include <string> #include <vector> #include "Hero.h" #include "Warrior.h" #include "Archmage.h" /** 游戲引擎/游戲業務/游戲核心類* **/ class GameCore { public:GameCore();~GameCore();// 定義一個函數,用來移動游戲角色// 重載--函數名相同,參數列表類型或數量不同void MoveRole(Warrior& warrior){warrior.Move(); // 實際上就是調用傳入戰士的移動方法}void MoveRole(Archmage& archmage){archmage.Move();}// 移動一批戰士void MoveRole(vector<Warrior*> vecWarrior){for(auto warrior:vecWarrior){warrior->Move();}} }; #endif //CHAPTER14_GAMECORE_H //main.cpp #include <iostream> #include <string> #include <vector> #include "Hero.h" #include "Warrior.h" #include "Archmage.h" #include "GameCore.h" using namespace std; void HeroTest(); int main() {HeroTest();return 0; }void HeroTest(){Hero hero("布衣");Warrior warrior1("呂布1",50);Warrior warrior2("呂布2",50);Warrior warrior3("呂布3",50);Archmage archmage("甘道夫",80);GameCore gamecore; // gamecore.MoveRole(warrior); // gamecore.MoveRole(archmage);vector<Warrior *> vecWarrior;vecWarrior.push_back(&warrior1);vecWarrior.push_back(&warrior2);vecWarrior.push_back(&warrior3);// 主要觀察,調用游戲業務方法來統一操作傳入的多個戰士gamecore.MoveRole(vecWarrior);}

輸出

調用了Hero 四個參數版本的構造 調用了Hero 一個參數版本的構造 調用了Hero 四個參數版本的構造 調用了Hero 四個參數版本的構造 調用了Hero 四個參數版本的構造 調用了Hero 四個參數版本的構造 戰士《呂布1》背著一大堆近戰武器正在前進。。。 戰士《呂布2》背著一大堆近戰武器正在前進。。。 戰士《呂布3》背著一大堆近戰武器正在前進。。。

2.動態多態-重寫

動態多態–重寫
動態多態也叫運行時多態,函數在執行的過程中才能確定要執行的是哪一個。

父類方法中加virtual關鍵字,在核心引擎類中的RoleMove參數使用hero 對象,那么可以給RoleMove傳遞各種hero子類實現各種移動。

//mian.cpp #include <iostream> #include <string> #include <vector> #include "Hero.h" #include "Warrior.h" #include "Archmage.h" #include "GameCore.h" #include "Assassin.h" using namespace std; void HeroTest(); int main() {HeroTest();return 0; }void HeroTest(){Hero hero("布衣");Warrior warrior1("呂布1",50);Warrior warrior2("呂布2",50);Warrior warrior3("呂布3",50);Archmage archmage("甘道夫",80);GameCore gamecore;// 不使用virtual 關鍵字的效果// 編譯器就會根據當前對象的類型,調用類型中定義的move 方法gamecore.MoveRole(warrior1);gamecore.MoveRole(archmage);// 使用virtual 關鍵字,派生類重寫了基類的方法//不使用virtual 輸出// 普通英雄呂布1正在奔跑在艾澤拉斯大陸上// 普通英雄甘道夫正在奔跑在艾澤拉斯大陸上//使用virtual 輸出// 戰士《呂布1》背著一大堆近戰武器正在前進。。。// 大法師甘道夫為了節省魔法, 只好用雙腳趕路// 不修改核心邏輯,直接傳入新類型對象Assassin assa("飛檐走壁",100);gamecore.MoveRole(assa); }

新增的刺客類

// Assassin.h // // Created by 陳瑩瑩 on 2021/3/25. //#ifndef CHAPTER14_ASSASSIN_H #define CHAPTER14_ASSASSIN_H #include <iostream> #include <string> #include "Hero.h" using namespace std; /** 體會程序是如何進行升級的* 假定游戲需要增加一個新的職業:刺客,但是核心業務類肯定不能夠隨便修改*/class Assassin:public Hero{ public:Assassin();Assassin(const string& nickName, int power):Hero(nickName),m_Power(power){}void Move() override{cout << "隱藏在黑暗中的刺客" << GetNickName() << "正在偷偷地潛入一座宮殿"<< endl;}~Assassin(); private:int m_Power;};#endif //CHAPTER14_ASSASSIN_H //Assassin.h // // Created by 陳瑩瑩 on 2021/3/25. //#include "Assassin.h"Assassin::Assassin() {} Assassin::~Assassin(){}

2.1 向上轉換/向下轉換

// 為了能夠讓同一個函數操作不同類型的子類對象,所以我們把參數類型定義成基類對象// 當傳遞Hero類型的子類型時,參數類型可以自動轉換// 關于向上和向下轉換// 當B是A的子類型(class B: public A ),意味著所有對A對象的操作都可以對B對象進行// 即B重用A的操作來實現自己的操作// 向上轉型:把子類型對象轉換為父類型對象,下面有三個注意點:// 1.向上轉型是安全的// 2.向上轉型是自動完成的(自動類型轉換)// 3.向上轉型的過程中,會丟失子類型的信息。// Warrior warrior; // 子類型對象// Hero& hero = warrior; // 父類型引用指向了子類型對象--向上轉型// hero.XiaoQuanQuan(); // 編譯器會報錯--丟失了子類型信息// 如果還想使用子類型方法,那么就需要再進行強制類型轉換--向下轉型// warrior& newWarrior = (Warrior&)hero; // 向下轉型不安全// hero對象有可能是父類型的另一個子類型// Archmage warrior;// Hero& hero = warrior;// Warrior& newWarrior = (Warrior&)hero; // 編譯時不會報錯,但是執行時會報錯,(老師演示的時候還能夠運行的)

3.虛函數的工作原理

1.構造函數不能是虛函數
2.析構函數應該定義成虛函數,除非該類不做基類。為了安全起見,為將類的析構函數定義為虛函數。
3.友元函數不能是虛函數。

虛函數的工作原理:會為父類對象構建一個隱藏成員,為指向虛函數表的指針。子類重寫了父類方法的話,也會為子類對象構建一個隱藏成員,為指向虛函數表的指針。但是具體的函數指針變了的。

demo1:觀察虛函數列表地址的變化(實驗現象沒有實現)

//mian.cpp void VirtualPointTest(){ // Base base; // 基類對象 // long* baseAdress = (long*) &base; // 轉換成長整形指針,方便待會指針移動和轉換 // // cout << "基類對象地址" << &base << endl; // cout << "基類對象地址" << baseAdress << endl; // long* virTablePtr = (long*)(baseAdress + 0); // 虛函數表的地址就是這么求的 // cout << "虛函數表的地址:" << virTablePtr << endl; // long* virFunctionPtr1 = (long*) *(virTablePtr + 0); // cout << "虛函數表中第一個虛函數的地址" << virFunctionPtr1 << endl; // long* virFunctionPtr2 = (long*) *(virTablePtr + 1); // cout << "虛函數表中第一個虛函數的地址" << virFunctionPtr1 << endl; // long* virFunctionPtr3 = (long*) *(virTablePtr + 2); // cout << "虛函數表中第一個虛函數的地址" << virFunctionPtr1 << endl;Base base; // 基類對象int* baseAdress = (int*)&base; // 基類對象cout << "基類對象地址" << baseAdress << endl; // 保存基類對象的地址int* virTablePtr = (int*)*(baseAdress + 0); //虛擬表的指針地址cout << "基類隱藏成員:虛擬表的指針地址:" << virTablePtr << endl; // // 虛擬表中第一個虛函數的地址 // int* virFunctionPtr = (int*) *(virTablePtr + 0); // cout << "虛擬表中第一個虛函數的地址:" << virFunctionPtr << endl; //沒輸出成功呀 // cout << "end" << endl; // //強制轉換成函數來調用 // void(*BaseVirtual1)() = (void(*)())virFunctionPtr; // BaseVirtual1(); // 取出第一個虛函數后調用。 // // 下面注意:GCC mingW64 指針+ 2,如果使用的是VS20xx版本,指針需要加1, mac gcc +2 // int* virFunctionPtr2 = (int*) *(virTablePtr + 2); // void(*BaseVirtual2)() = (void(*)())virFunctionPtr2; // BaseVirtual2(); // 取出第一個虛函數后調用。 // int* virFunctionPtr3 = (int*) *(virTablePtr + 4); // void(*BaseVirtual3)() = (void(*)())virFunctionPtr3; // BaseVirtual3(); // 取出第一個虛函數后調用。// 取出第一私有成員cout << "第一個私有成員member的值:" << *(baseAdress + 2) << endl; // 9527 取處出來了cout << "---------- 派生類對象的內存信息如下----------------" << endl;Son son;int* sonAdress = (int*)&son;cout << "派生類對象的地址:" << sonAdress << endl;virTablePtr = (int*)*(sonAdress + 0);cout << "派生類對象的虛擬表的地址:" << virTablePtr << endl;// 有三個虛函數,一個被覆蓋了(地址變了),其余兩個沒有變。 } // VirtualPointDemo1.h // // Created by 陳瑩瑩 on 2021/3/27. // #ifndef CHAPTER14_VIRTUALPOINTDEMO1_H #define CHAPTER14_VIRTUALPOINTDEMO1_H #include <iostream> #include <string> using namespace std; class Base { private:int menber; public:Base(){menber = 9527;}virtual void baseVirtual1(){cout << "基類中的虛函數版本1"<<endl;}virtual void baseVirtual2(){cout << "基類中的虛函數版本2"<<endl;}virtual void baseVirtual3(){cout << "基類中的虛函數版本3"<<endl;} };class Son :public Base{ public:void baseVirtual2() override{cout << "派生類中唯一實現的2版本的基類虛函數" << endl;} };#endif //CHAPTER14_VIRTUALPOINTDEMO1_H

4.純虛函數和抽象類

抽象類–天生的父類,實例出來沒啥用,需要進行擴展。(生物對象:血量,攻擊力)

語法上一個抽象類無法被實例化

抽象類的虛函數都為純虛函數,純虛函數讓基類函數沒有函數體,在基類中不能被調用。純虛函數必須有派生類來實現純虛函數體的功能。(一個類如果有一個純虛函數,那么這個類就是抽象類)

純虛函數語法格式

virtual 返回類型 函數名(參數列表) const=0;

demo:多態的方式來模擬“星際爭霸”中的指揮官和各種兵種之間的互動關系。
指揮官發出指令–Rolling Thunder,各單位發起進攻

//mian.cpp #include <iostream> #include <vector> #include "AbstractClass.h"void AbstractTest(); int main() {AbstractTest();return 0; }void AbstractTest(){// 嘗試實例化一個抽象類類// BattleUnit battleUnit; 提示是一個抽象類不能被實例化// 沒有重載全部虛函數,子類還是會被認為是抽象類Marin marin1("巫妖王");Marin marin2("死亡騎士");marin1.Fight(marin2);SiegeTank tank1("坦克1");tank1.Move(10,20);Viking viking1("北歐海盜");vector<BattleUnit*> units;units.push_back(&marin1);units.push_back(&marin2);units.push_back(&tank1);units.push_back(&viking1);Commander commander;cout << "讓指揮官移動多個不同類型的戰斗單位" << endl;commander.Move(units,50,50); } //AbstractClass.h // // Created by 陳瑩瑩 on 2021/4/2. // #ifndef STAR_WAR_ABSTRACTCLASS_H #define STAR_WAR_ABSTRACTCLASS_H #include <iostream> #include <string> #include <vector> using namespace std; /** 實現一個簡單版的星際爭霸游戲,用來加深對多態及抽象類的理解* */ class Point{ private:int m_x;int m_y; public:Point(){}Point(int _x, int _y):m_x(_x),m_y(_y){}int GetX() {return m_x;}int GetY() {return m_y;}void SetX(int x) {this->m_x = x;}void SetY(int y) {this->m_y = y;}friend ostream& operator << (ostream& out, const Point& p){out << "(" << p.m_x << "," << p.m_y << ")" << endl;return out;} };class BattleUnit{// 戰斗單位了 private: protected:string name;int maxHp;int currHp;Point position;int attDistance; // 當前對象的攻擊距離 public:BattleUnit(){}BattleUnit(const string& _name): name(_name){maxHp = 100;currHp = 100;position.SetX(0);position.SetY(0);attDistance = 100;}// 設置某個方法分為純虛函數,Battle類變成抽象類,不能實例化virtual void Fight(BattleUnit& other) = 0;virtual void Move(int x, int y) = 0;virtual void Move(Point& position) = 0;const string & GetName() const{return name;} }; // 我們可以提供抽象的基類純虛方法的默認實現 void BattleUnit::Fight(BattleUnit& other){// 每個單位進行對戰前,依據當前坐標計算兩個單位間的距離// 如果距離超過的攻擊距離,攻擊失敗。cout << name << "正在攻擊另一個戰斗單位:" << other.GetName() << endl; } void BattleUnit::Move(int x, int y){position.SetX(x);position.SetX(y); }class Marin:public BattleUnit{ public:Marin(){}Marin(const string& _name):BattleUnit(_name){}void Fight(BattleUnit& other) override;void Move(int x, int y){BattleUnit::Move(x,y);cout << "陸戰隊員接到命令,立即前往坐標點: " << position << endl;}void Move(Point& position){} }; void Marin::Fight(BattleUnit& other){// 在子類中調用父類的同名方法,需要使用到域運算符BattleUnit :: Fight(other);cout << "陸戰隊員" << GetName() << "正在攻擊敵人:" << other.GetName() << endl; }class SiegeTank : public BattleUnit{ public:SiegeTank(){}SiegeTank(const string& _name) : BattleUnit(_name){}// undifined reference to "Vtable" for SiegeTank // 沒有實現完全父類的純虛函數void Fight(BattleUnit& other) override{}void Move(int x, int y)override{position.SetX(x);position.SetY(y);cout << "工程坦克" << GetName() << "收到移動命令:" << position << endl;}void Move(Point& position)override{} }; class Viking : public BattleUnit{ public:Viking(){}Viking(const string& _name) : BattleUnit(_name){}void Fight(BattleUnit& other) override{}void Move(int x, int y)override{position.SetX(x);position.SetY(y);cout << "維京戰機" << GetName() << "立即飛往坐標:" << position << endl;}void Move(Point& position)override{} };class Commander{// 游戲中的核心業務類,引擎 public:// 模擬了指揮官的rolling thunder// 一個指揮官同時移動了多個戰斗單位void Move(vector<BattleUnit*> units, int x, int y){for(auto unit : units){unit->Move(x,y);}} }; #endif //STAR_WAR_ABSTRACTCLASS_H

5.補充項目(都市浮生記)-卒

window 編程呀,mac 的頭文件都引入不了

總結

以上是生活随笔為你收集整理的C++(23)--多态性与虚函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。