在本文中,您將了解虛函數(shù)及其使用位置。此外,您還將學(xué)習(xí)純虛函數(shù)和抽象類。
虛函數(shù)是基類中的成員函數(shù),您希望在派生類中重新定義這些函數(shù)。
在詳細(xì)介紹之前,讓我們先了解一下為什么首先需要虛函數(shù)。
讓我們假設(shè),我們正在開發(fā)一個游戲(比如道具:武器)。
我們創(chuàng)建了Weapon該類并派生了兩個類,Bomb和Gun加載了各自武器的功能。
#include <iostream> using namespace std; class Weapon { public: void loadFeatures() { cout << "載入武器特性。\n"; } }; class Bomb : public Weapon { public: void loadFeatures() { cout << "裝載刀的特性。\n"; } }; class Gun : public Weapon { public: void loadFeatures() { cout << "裝載槍的特性\n"; } }; int main() { Weapon *w = new Weapon; Bomb *b = new Bomb; Gun *g = new Gun; w->loadFeatures(); b->loadFeatures(); g->loadFeatures(); return 0; }
輸出結(jié)果
裝載武器特性。 裝載刀的特性。 裝載槍的特性。
我們分別定義了Weapon,Bomb和Gun類的三個指針對象w,b和g。 并且,我們使用以下命令調(diào)用每個對象的loadFeatures()成員函數(shù):
w->loadFeatures(); b->loadFeatures(); g->loadFeatures();
完美的作品!
但是,我們的游戲項目開始變得越來越大。并且,我們決定創(chuàng)建一個單獨的Loader類來加載武器功能。
此類Loader根據(jù)選擇的武器加載武器的其他功能。
class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } };
loadFeatures()負(fù)載特定武器的特征。
#include <iostream> using namespace std; class Weapon { public: Weapon() { cout << "裝載武器特性。\n"; } void features() { cout << "裝載武器特性。\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "裝載刀的特性。\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "加載槍的特性。\n"; } }; class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } }; int main() { Loader *l = new Loader; Weapon *w; Bomb b; Gun g; w = &b; l->loadFeatures(w); w = &g; l->loadFeatures(w); return 0; }
輸出結(jié)果
裝載武器特性。 裝載武器特性。 裝載武器特性。 裝載武器特性。
我們的實現(xiàn)似乎是正確的。但是,裝載武器特性被加載了4次。為什么?
最初,武器對象w指向(Bomb)類的b對象。 并且,我們嘗試使用l對象指向(Loader類的)指針將其傳遞給loadFeatures()函數(shù)來加載Bomb對象的特性。
同樣,我們嘗試加載Gun對象的特性。
但是,Loader類的loadFeatures()函數(shù)將指向Weapon類對象的指針作為參數(shù):
void loadFeatures(Weapon *weapon)
這就是為什么武器特性被加載4次的原因。為了解決這個問題,我們需要使用virtual關(guān)鍵字實現(xiàn)基類(Weapon類)的虛函數(shù)。
class Weapon { public: virtual void features() { cout << "裝載武器特性。\n"; } };
#include <iostream> using namespace std; class Weapon { public: virtual void features() { cout << "裝載武器特性。\n"; } }; class Bomb : public Weapon { public: void features() { this->Weapon::features(); cout << "裝載刀的特性。\n"; } }; class Gun : public Weapon { public: void features() { this->Weapon::features(); cout << "加載槍的特性。\n"; } }; class Loader { public: void loadFeatures(Weapon *weapon) { weapon->features(); } }; int main() { Loader *l = new Loader; Weapon *w; Bomb b; Gun g; w = &b; l->loadFeatures(w); w = &g; l->loadFeatures(w); return 0; }
輸出結(jié)果
裝載武器特性。 裝載刀的特性。 裝載武器特性。 加載槍的特性。
另外,注意,l->loadFeatures(w)函數(shù)會根據(jù)l對象所指向的對象調(diào)用不同類的函數(shù)。
使用虛函數(shù)使我們的代碼不僅更加清晰,而且更加靈活。
在以上程序中,“裝載武器特性?!北淮蛴×藘纱巍N覀兘ㄗh您在上述程序上添加其他代碼,以便只加載一次武器特性。
如果我們想添加另一種武器(比如說 弓),我們可以輕松地添加和加載其特性。如何添加?
class Bow : public Weapon { public: void features() { this-<Weapon::features(); cout >> "加載弓的特性。\n"; } };
并且,在main()函數(shù)中添加如下代碼。
Bow b; w = &b; l->loadFeatures(w);
值得注意的是,我們沒有更改Loader類中的任何內(nèi)容來加載刀的特性。
面向?qū)ο缶幊痰哪康氖菍⒁粋€復(fù)雜的問題分成幾個小集合。這有助于有效理解和處理問題。
有時,最好僅在更好地可視化問題的情況下使用繼承。
在C ++中,您可以創(chuàng)建一個無法實例化的抽象類(您不能創(chuàng)建該類的對象)。但是,您可以從中派生一個類并實例化派生類的對象。
抽象類是無法實例化的基類。
包含純虛函數(shù)的類稱為抽象類。
聲明以結(jié)尾的=0虛函數(shù)稱為純虛函數(shù)。例如,
class Weapon { public: virtual void features() = 0; };
在這里,純虛函數(shù)是
virtual void features() = 0
并且,該類Weapon是抽象類。
#include <iostream> using namespace std; // 抽象類(不允許實例化的類) class Shape { protected: float l; public: void getData() { cin >> l; } // 虛函數(shù) virtual float calculateArea() = 0; }; class Square : public Shape { public: float calculateArea() { return l*l; } }; class Circle : public Shape { public: float calculateArea() { return 3.14*l*l; } }; int main() { Square s; Circle c; cout << "輸入長度來計算正方形的面積: "; s.getData(); cout<<"正方形的面積: " << s.calculateArea(); cout<<"\n輸入半徑以計算圓的面積: "; c.getData(); cout << "圓的面積: " << c.calculateArea(); return 0; }
輸出結(jié)果
輸入長度來計算正方形的面積: 4 正方形的面積: 16 輸入半徑以計算圓的面積: 5 圓的面積: 78.5
在此程序中,純虛函數(shù)virtual float area()= 0; 在Shape類中定義。
需要注意的一件事是,您應(yīng)該在派生類中重寫基類的純虛函數(shù)。 如果重寫失敗,則派生類也將成為抽象類。