重載、覆蓋和隱藏的區別以及基類、父類指針關係

函數重載:

在一個類中,函數名相同,函數參數類型,或者函數參數個數不同,或者都不同,成爲函數重載。
函數的重載發生在程序編譯階段,由編譯器進行優化和區分。
《高質量C++/C編程指南》已經清晰的列出了重載函數的特性:

(1)相同的範圍(在同一個類中);
(2)函數名字相同;
(3)參數不同;
(4)virtual關鍵字可有可無。

如:

class A
{
public:
    A()
    {
        age = 3;
    }
    A(int num)
    {
        age = num;
    }
private:
    int age;
};

如A()與A(int num)同爲A的構造函數,但由於參數不同,則兩則互爲重載,對於一個實例,編譯器會根據參數的個數執行不同的構造函數


函數的隱藏和覆蓋只發生在子類和父類之間,函數隱藏發生在編譯時期,相對於對象指針爲固定偏移,函數覆蓋發生在運行時期,基類和子類的虛函數會根據虛函數列表到指向出運行虛函數。

對於父類和子類中同名函數,如果沒有virtual關鍵字修飾,編譯器都將子類中的基類同名函數做隱藏處理;如果有virtual關鍵字修飾,編譯器會建立虛函數列表,運行時期,根據虛函數表運行相應的虛函數。


函數隱藏

函數隱藏,是指派生類函數將基類函數給藏起來了,即函數隱藏發生在子類和父類之間。特性爲:

  1. 如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無virtual關鍵字,基類的函數將被隱藏。
  2. 如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有virtual關鍵字。此時,基類的函數別隱藏。
#include <iostream>
using namespace std;
class A
{
public:
    void foo()
    {
        printf("1\n");
    }
};
class B :public A
{
public:
    void foo()
    {
        printf("3\n");
    }
};
int main(void)
{
    A a;
    A *p_A = &a;
    p_A->foo();//執行成員函數

    B b;
    B *p_B = &b;
    p_B->foo();//執行成員函數

    p_A = &b;   //p_A指針執行子類,可以訪問被隱藏的基類成員函數
    p_A->foo();

    system("pause");
    return 0;
}

輸出爲1 3 1
執行成員函數的輸出爲1,3,這裏沒有問題。B中成員函數foo()與基類A中成員函數foo,函數名相同,且沒有virtual關鍵字,則子類會將基類中的同名函數隱藏,但是,通過用基類指針指向子類,可以訪問被隱藏的基類同名函數foo().
同時也說明,除數據區以外,子類會繼承基類所有的成員函數,但對於同名的成員函數,子類在實現自身的成員函數後,會將基類的同名函數隱藏(這裏的隱藏指不能通過子類型的對象指針訪問到基類的隱藏指針,但可以通過基類指針訪問到被隱藏的子類指針中的基類指針)
子類父類中成員函數在內存中的排布
圖爲子類父類中成員函數在內存中的排布

函數覆蓋

函數的覆蓋是指派生類函數覆蓋基類函數,只作用與派生類函數,其特性爲:

  1. 不同的範圍(分別位於派生類與基類);
  2. 函數名字相同;
  3. 參數相同;
  4. 基類函數必須有virtual關鍵字。
    實際上虛函數的作用,就是實現覆蓋
    函數的覆蓋發生在執行時期,對於有虛函數的類,都含有虛函數表,其虛函數表的指針偏移量固定,但表中函數所指向的函數地址不同,運行時期,會根據虛函數表去執行對應的虛函數。
#include<iostream>  
using namespace std;

class A
{
public:
    void foo()
    {
        printf("1\n");
    }
    virtual void fun()
    {
        printf("2\n");
    }
};
class B : public A
{
public:
    void foo()
    {
        printf("3\n");
    }
    void fun()
    {
        printf("4\n");
    }
};

int main(void)
{
    A a;
    B b;
    A *p = &a;
    p->foo();   //訪問基類成員函數
    p->fun();   //訪問基類虛函數

    p = &b;
    p->foo();   //訪問子類成員函數
    p->fun();   //訪問基類虛函數

    B *ptr = (B *)&a;  
    ptr->foo();  //訪問子類成員函數
    ptr->fun(); //訪問基類成員函數

    system("pause");
    return 0;
}

輸出爲1 2 1 4 3 2
基類和子類中的同名函數,根據是否有virtual關鍵字修飾分爲成員函數和虛函數,對於成員函數基類做隱藏處理,對於虛函數,子類重新實現,並更新虛函數表,基類和子類都是通過訪問虛函數表間接訪問虛函數。
即,對象指針訪問成員函數時,會根據指針的類型,偏移確定的間隔,子類可以通過跳轉基類的偏移來訪問子類中被隱藏的基類同名函數;對象指針訪問虛函數時,會根據虛函數表的指向去執行虛函數。
這裏寫圖片描述

發佈了19 篇原創文章 · 獲贊 30 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章