C++入門--函數覆蓋


  • 函數重載
  1. 函數名相同
  2. 參數列表(個數/類型/順序)不同
  3. 相同作用域
  4. 函數重載不考慮返回值的不同
  • 函數隱藏
  1. 作用域不同
  2. 函數名相同
  3. 參數和返回值不考慮
  • 函數覆蓋(虛函數)
  1. 作用域不同(父子類之間的繼承關系)
  2. 函數名,參數列表(參數個數/順序/類型),返回值,調用約定必須相同
  3. 有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);

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM