- 函數重載
- 函數名相同
- 參數列表(個數/類型/順序)不同
- 相同作用域
- 函數重載不考慮返回值的不同
- 函數隱藏
- 作用域不同
- 函數名相同
- 參數和返回值不考慮
- 函數覆蓋(虛函數)
- 作用域不同(父子類之間的繼承關系)
- 函數名,參數列表(參數個數/順序/類型),返回值,調用約定必須相同
- 有virtual關鍵字
看一個例子:
1 #include <stdlib.h> 2 3 #include <iostream> 4 #include <string> 5 using namespace std; 6 7 class CPerson { 8 public: 9 void speak() { cout << "speak" << endl; } 10 }; 11 12 class CChinese : public CPerson { 13 public: 14 virtual void speak() { cout << "speak Chinese" << endl; } 15 }; 16 17 int main(int argc, char const* argv[]) { 18 19 CPerson per; 20 int nPersonSize = sizeof(CPerson); 21 cout << nPersonSize << endl; 22 23 return 0; 24 }
CPerson類僅有一個非虛函數的speak方法時,獲取CPerson的大小
nPersonSize = 1; //1字節為一個占位符
將CPerson的speak聲明為virtual虛函數
virtual void speak() { cout << "speak" << endl; }
此時CPerson的大小:
nPersonSize = 4; //雖然沒有成員變量,但是有virtual關鍵字,有4字節的虛表指針
二、虛表中虛函數的順序:
1.子類繼承了所有父類虛函數(公有)
2.父類的虛函數順序決定了子類虛函數的順序
3.子類重寫了父類的某虛函數,則會在子類自己的虛表中覆蓋對應位置的函數
4.子類未重寫某虛函數,則直接繼承父類的該虛函數
5.子類自己的虛函數會出現在前面父類所有虛函數后面
三、虛函數的直接調用與間接調用
1、直接調用
根據函數名稱,直接調用該函數(編譯器在編譯時候就確定了其跳轉地址)
1)普通函數的調用
2)對象的普通成員函數的調用
3)對象的虛函數的調用
2、間接調用(虛調用,通過查虛表來調用)
虛函數,通過查找對象的虛表下標來調用函數的方法(在運行時期確定調用誰)
1)通過對象的指針調用虛函數
CChinese* pChs = &chs;
pChs->foo();
2)通過對象的引用調用虛函數
CChinese& pChs = &chs;
pChs->foo();
3)以上兩情況下,若明確了類域范圍,則為直接調用
pChs->CChinese::foo();
四、函數覆蓋隱藏重載
看以下示例,判斷main輸出結果
#include <stdlib.h> #include <iostream> #include <string> using namespace std; class Base { public: virtual void Handle1(float x) { cout << "Base::Handle1(float)" << endl; } void Handle2(float x) { cout << "Base::Handle2(float)" << endl; } void Handle3(float x) { cout << "Base::Handle3(float)" << endl; } }; class Derived : public Base { public: virtual void Handle1(float x) { cout << "Derived::Handle1(float)" << endl; } void Handle2(int x) { cout << "Derived::Handle2(int)" << endl; } void Handle3(float x) { cout << "Derived::Handle3(float)" << endl; } void Handle3(double x) { cout << "Derived::Handle3(double)" << endl; } }; int main(int argc, char const *argv[]) { Derived DervObj; //定義了一個子類的對象 //把子類對象傳遞給了父類,得到父類指針,子類指針強轉父類指針是安全的 Base *pBase = &DervObj; Derived *pDerv = &DervObj; //得到子類指針 pBase->Handle1(3.14f); pDerv->Handle1(3.14f); pBase->Handle2(3.14f); pDerv->Handle2(3.14f); pBase->Handle3(3.14f); pDerv->Handle3(3.14f); pDerv->Handle3(3.14); return 0; }
分析以下:
- 子類的1與父類的handle1,作用域不同(父子繼承關系) ,函數名參數返回值都相同,且為虛函數,故為函數覆蓋
- 看父子的Handle2、3,作用域不同,函數名相同,參數不管,為函數隱藏
- 看子類的Handle3,作用域相同,函數名相同,參數不同,為函數重載
// 間接調用:查表(查找子類的虛表),子類對象有函數覆蓋,調用子類的Handle1 pBase->Handle1(3.14f); //間接調用:查表(查找子類的虛表),子類對象有函數覆蓋,調用子類的Handle1 pDerv->Handle1(3.14f);