C++多态和虚函数
我們直觀上認為,如果指針指向了派生類對象,那么就應該使用派生類的成員變量和成員函數,這符合人們的思維習慣。但是本例的運行結果卻告訴我們,當基類指針 p 指向派生類 Teacher 的對象時,雖然使用了 Teacher 的成員變量,但是卻沒有使用它的成員函數,導致輸出結果不倫不類(趙宏佳本來是一名老師,輸出結果卻顯示人家是個無業游民),不符合我們的預期。
換句話說,通過基類指針只能訪問派生類的成員變量,但是不能訪問派生類的成員函數。
為了消除這種尷尬,讓基類指針能夠訪問派生類的成員函數,C++ 增加了虛函數(Virtual Function)。使用虛函數非常簡單,只需要在函數聲明前面增加 virtual 關鍵字。
更改上面的代碼,將 display() 聲明為虛函數:
#include <iostream> using namespace std; //基類People class People{ public:People(char *name, int age);virtual void display(); //聲明為虛函數 protected:char *m_name;int m_age; }; People::People(char *name, int age): m_name(name), m_age(age){} void People::display(){cout<<m_name<<"今年"<<m_age<<"歲了,是個無業游民。"<<endl; } //派生類Teacher class Teacher: public People{ public:Teacher(char *name, int age, int salary);virtual void display(); //聲明為虛函數 private:int m_salary; }; Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){} void Teacher::display(){cout<<m_name<<"今年"<<m_age<<"歲了,是一名教師,每月有"<<m_salary<<"元的收入。"<<endl; } int main(){People *p = new People("王志剛", 23);p -> display();p = new Teacher("趙宏佳", 45, 8200);p -> display();return 0; } 王志剛今年23歲了,是個無業游民。 趙宏佳今年45歲了,是一名教師,每月有8200元的收入。和前面的例子相比,本例僅僅是在 display() 函數聲明前加了一個virtual關鍵字,將成員函數聲明為了虛函數(Virtual Function),這樣就可以通過 p 指針調用 Teacher 類的成員函數了,運行結果也證明了這一點(趙宏佳已經是一名老師了,不再是無業游民了)。
????有了虛函數,基類指針指向基類對象時就使用基類的成員(包括成員函數和成員變量),指向派生類對象時就使用派生類的成員。換句話說,基類指針可以按照基類的方式來做事,也可以按照派生類的方式來做事,它有多種形態,或者說有多種表現方式,我們將這種現象稱為多態(Polymorphism)。
上面的代碼中,同樣是p->display();這條語句,當 p 指向不同的對象時,它執行的操作是不一樣的。同一條語句可以執行不同的操作,看起來有不同表現方式,這就是多態。
????多態是面向對象編程的主要特征之一,C++中虛函數的唯一用處就是構成多態。
????C++提供多態的目的是:可以通過基類指針對所有派生類(包括直接派生和間接派生)的成員變量和成員函數進行“全方位”的訪問,尤其是成員函數。如果沒有多態,我們只能訪問成員變量。
前面我們說過,通過指針調用普通的成員函數時會根據指針的類型(通過哪個類定義的指針)來判斷調用哪個類的成員函數,但是通過本節的分析可以發現,這種說法并不適用于虛函數,虛函數是根據指針的指向來調用的,指針指向哪個類的對象就調用哪個類的虛函數。
但是話又說回來,對象的內存模型是非常干凈的,沒有包含任何成員函數的信息,編譯器究竟是根據什么找到了成員函數呢?我們將在《C++虛函數表精講教程,直戳多態的實現機制》一節中給出答案。
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
- 上一篇: 将派生类指针赋值给基类的指针
- 下一篇: 多态的用途