初識C++面向物件特性——多型
個人理解,用一句話來概括多型就是:一個介面(函式)能實現不同種方法
C++的多型分為靜態多型和動態多型
靜態多型:指的就是過載(編譯的時候函式地址就已經確定)
動態多型:繼承並重寫基類的虛擬函式
關於靜態多型(過載),比較簡單,下面用兩段程式碼來演示一下
#include<iostream> #include<string> using namespace std; void PrintInp(int a); void PrintInp(string a); int main(void) { int a; cin >> a; PrintInp(a);string str; cin >> str; PrintInp(str); system("pause"); return 0; } void PrintInp(int a) { cout << a << endl; } void PrintInp(string a) { cout << a << endl; }
第一段程式碼通過過載實現了函式的多型
#include<iostream> using namespace std; class A { public:void func1() { cout << "class A, func1" << endl << endl; } }; class B :public A { public: void func1() { cout << "class B, func1" << endl << endl; } }; int main(void) { A a; B b; a.func1(); b.func1(); system("pause"); return 0; }
第二段程式碼通過過載實現了類中成員函式的(靜態)多型
為什麼要使用虛擬函式來實現動態多型呢,因為存在這種情況:父類想通過多型的特性使用子類的成員函式,如下面程式碼所示
#include<iostream> using namespace std; class A { public: void func1() { cout << "class A, func1" << endl << endl; } virtual void func2() { cout << "class A, func2" << endl << endl; } }; class B :public A { public: void func1() { cout << "class B, func1" << endl << endl; } }; class C :public A { public: void func2() { cout << "class C, func2" << endl << endl; } }; int main(void) { A a; B b; a.func1(); b.func1(); A* Test = new B; Test->func1(); A* VirTest = new C; VirTest->func2(); system("pause"); return 0; }
執行結果如下
可以看到Test想通過多型特性使用指標呼叫子類B中的成員函式,但是他呼叫的仍然是他自己的func1(),為什麼會這樣?就要從動態多型的實現原理——虛表說起
https://blog.csdn.net/qq_40840459/article/details/80195158
弄清了虛表結構之後回到上面的程式碼,通過虛擬函式我們能實現什麼操作?虛擬函式該怎麼用來實現(動態)多型?
首先通過虛擬函式,父類可以呼叫子類的成員函式
使用方法:
FATHER* A=new SON; A->function();
此時類A呼叫的function()就是子類中的function
要注意的是虛擬函式只能通過指標或者引用來達到多型效果,直接例項化一個類然後直接呼叫虛擬函式是無法實現多型的
解釋一下為什麼基類解構函式要寫成虛擬函式
FATHER* A=new SON; delete A;
實際上,在編譯器中,解構函式會被特殊處理,他們也是具有多型性的函式。A的型別是子類,所以析構A的時候需要用SON的解構函式,但是如果父類中的解構函式沒有使用虛擬函式,那麼delete A在啟動虛構函式時,會直接呼叫父類的解構函式,new SON申請到的空間就沒有得到回收,會造成記憶體洩漏。
關於虛擬函式對應虛表的地址操作和純虛擬函式的相關知識可以查閱下面連結
https://blog.csdn.net/qq_37668377/article/details/81007527
總結一下多型的優點
可替換、可擴充套件、靈活、使用簡單以及介面性(介面行是什麼意思還沒弄清)