一、多態有靜態多態和動態多態:
1、靜態多態:函數重載和運算符重載屬於靜態多態,復用函數名
2、動態多態:派生類和虛函數實現運行時多態
二、靜態多態和動態多態的區別
1、靜態多態函數地址早綁定:在編譯階段確定函數地址
2、動態多態的函數地址晚綁定:運行階段確定函數地址
三、動態多態滿足條件
1、有繼承關系
2、子類重寫父類虛函數
四、動態多態的使用
父類的指針或者引用 執行子類對象
代碼:
#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 5; class Animal { public: virtual void speak() { printf("Animal is Speaking\n"); } }; class dog : public Animal { public: virtual void speak() //這里的virtual可寫可不寫
{ printf("dog is Speaking\n"); } }; class cat : public Animal { public: virtual void speak() //這里的virtual可寫可不寫
{ printf("cat is Speaking\n"); } }; void test1(Animal &a) { a.speak(); } void test2(Animal *a) { a->speak(); } int main() { dog g; cat t; test1(t); dog * ptr = &g; test2(ptr); return 0; }
多態原理:
如果把上面main函數內容換成
int main() { Animal * a=NULL; a->speak(); return 0; } /* 輸出: Animal is Speaking */
上面代碼說明了就算沒有對變量a實例化一個對象,但是調用的方法仍然是看變量a的左邊(也就是a的類型)。但是多態中我們發現變量a調用的方法實際上和a的類型無關,這一點就需要把函數變成虛函數,也就是在函數前面加上關鍵字virtual。
例如Animal類,當它內部的speak函數不是虛函數的時候,你的sizeof(Animal)的值是等於1的(因為方法和變量是分開存儲的,而且Animal類沒有成員變量,這就相當於Animal是一個空對象)。但是當內存speak函數是虛函數的時候,你的sizeof(Animal)的值是等於4或者8(這要看操作系統,因為實際上這個時候類內部會出現一個指針vfptr(virtual function pointer 虛擬函數表指針),在32位操作系統上指針占4個字節)。
vfptr會指向一個虛擬函數表(vftable),虛擬函數表里面記錄的是這個類的虛函數的地址
我們使用visual studio查看類信息:
當我們的cat類沒有重寫Animal的speak方法時(這個時候會把父類的東西都繼承過來):
當cat類重寫Animal的speak方法時:
我們接着對之前那個代碼分析一下:
#include <bits/stdc++.h>
using namespace std; const int maxn = 1e5 + 5; class Animal { public: virtual void speak() //這多加了virtual { printf("Animal is Speaking\n"); } }; int main() { Animal * a=NULL; a->speak(); }
這個代碼的輸出是什么也沒有,為什么不加virtual就有輸出,加了就沒有輸出呢?
我認為加了virtual之后函數變成了虛函數,這個時候類內部會出現一個指針vfptr,但是你實例化的時候給a指針賦了一個NULL指針,也就是沒有給a分配內存,那么vfptr指針就沒有了,也就沒辦法找到vftable,也就沒辦法找到函數入口地址。(這是我認為的,不知道對不對,如果有錯的話,希望大家給我指出^_^)