C++函數名字的隱藏:重寫、重載、重定義


概要:

     C++中經常出現函數名字一樣,但參數列表或返回值不同的函數,要搞清楚函數的正確調用關系,需理清三個概念:重寫(override)、重載(overload)、重定義(redefine)。

一、三個基本概念

  1、重定義(redefine):派生類對基類的成員函數重新定義,即派生類定義了某個函數,該函數的名字與基類中的函數名字一樣。

                特點:(1)不在同一個作用域(分別位於基類、派生類)  (2)函數的名字必須相同  (3)對函數的返回值、形參列表無要求

                特殊情況:若派生類定義的該函數與基類的成員函數完全一樣(返回值、形參列表均相同),且基類的該函數為virtual,則屬於派生類重寫基類的虛函數。

                作用效果:若重新定義了基類中的一個重載函數,則在派生類中,基類中該名字的函數(即其他所有重載版本)都被自動隱藏,包括同名的虛函數。

  2、重載(overload):函數名字相同,但它的形參個數或者順序,或者類型不同,但是不能靠返回類型來判斷

              特點:(1)位於同一個類中  (2)函數的名字必須相同    (3)形參列表不同(可能是參數個數  or  類型  or  順序 不同),返回值無要求  

              特殊情況:若某一個重載版本的函數前面有virtual修飾,則表示它是虛函數。但它也是屬於重載的一個版本

                            不同的構造函數(無參構造、有參構造、拷貝構造)是重載的應用

              作用效果和原理:編譯器根據函數不同的參數表,將函數體與函數調用進行早綁定。重載與多態無關,只是一種語言特性,與面向對象無關。

  3、重寫(override):派生類重定義基類的虛函數,即會覆蓋基類的虛函數   (多態性)

            特點:(1)不在同一個作用域(分別位於基類、派生類)  (2)函數名、形參列表、返回值相同  (3)基類的函數是virtual

            特殊情況:若派生類重寫的虛函數屬於一個重載版本,則該重寫的函數會隱藏基類中與虛函數同名的其他函數。

            作用效果:父類的指針或引用根據傳遞給它的子類地址或引用,動態地調用屬於子類的該函數。這個晚綁定過程只對virtual函數起作用

                         具體原理是由虛函數表(VTABLE)決定的,在第三節介紹。

 

二、程序實例

1、兩個類:基類( 取名Test)和派生類( 取名XX)   名字不規范,哈哈隨便取得!

    

基類和派生類的結構
//Base class
class Test
{
public:
    int a;

    Test()
    {
        cout<<"Test() 無參構造函數!"<<endl;
    }

    Test(int data)
    {
        a = data;
        cout<<"Test(int data) 有參構造函數!"<<endl;
    }

    Test(const Test &tmp)
    {
        a = tmp.a;
        cout<<"Test 拷貝構造函數!!"<<endl;        
    }

    //基類中對函數名f,進行了重載。其中最后一個重載函數為虛函數
    void f()const
    {
        cout<<"調用 void Test::f()"<<endl;
    }

    //overload
    int f(int data) const
    {
        cout<<"調用 Test f(int data)"<<endl;
        return 1;
    }

       //overload    虛函數
    virtual double f(int dataA,int dataB)
    {
        cout<<"調用 Test f(int a,int b)"<<endl;
        return dataA*dataB/2.0;
    }

};

class  XX: public Test
{
public:
    Test atest;//先調用基類的構造函數,然后對象成員的構造函數,最后才是派生類的構造函數

    XX()
    {
        cout<<"XX() 無參構造函數被調用!"<<endl;
    }

    //對基類的函數名f,進行了重定義。則會隱藏基類中的其他f函數
    //redefine
    int  f() const
    {
        cout<<" 調用 XX f()函數"<<endl;
         return 1;
    }

    //重寫基類的虛函數
    //redefine   override
    double f(int dataA,int dataB)
    {
        cout<<"調用 XX f(int dataA,int dataB)函數"<<endl;
        return (dataA+dataB)/2.0;
    }
};

分析:基類class Test中定義了名為f的3個重載函數,其中最后一個是虛函數

         派生類class  XX中對f進行了重定義,所以會隱藏基類中名為f的版本。其中派生類的double f(int dataA,int dataB)屬於對虛函數的重寫

測試---主程序
int main()
{
//-----test 1------------------------
    cout<<"-------test 1------------"<<endl;
    //Base class
    Test aaTest;
       aaTest.f();
    aaTest.f(12);
    aaTest.f(10,20);

    //derived class
     XX d;
    d.f();
//    d.f(2); //error C2661: 'f' : no overloaded function takes 1 parameters
    d.f(10,20);

//--------test 2----------------------------------
    cout<<"-------test 2------------"<<endl;
    Test b = d;
    b.f(); 
    b.f(10,20);//調用的是基類的函數,不發生多態

//--------test 3----------------------------------------
    cout<<"-------test 3------------"<<endl;
    Test &bR = d;//引用
    b.f();//f()不是虛函數,調用基類的函數
    bR.f(10,20);//調用的是派生類的函數,發生多態

//--------test 4--------------------------------------
    cout<<"-------test 4------------"<<endl;
    Test* pB = &d;
    b.f();
    pB->f(10,20);//調用的是派生類的函數,發生多態

    return 1;
}

分析:(1)test 1中進行了重載測試,根據傳遞參數的不一樣,調用不同的函數  (早綁定,與多態無關)

         (2)test 2中Test b = d;定義了一個基類對象,用派生類對象來進行初始化。這會調用基類的拷貝構造函數,生成基類的對象b,基類的拷貝構造函數初始化b的VPTR,指向b的VTABLE。因此所有的函數調用都只發生在基類,不會產生多態。

            這是一個對象切片過程(參見《C++編程思想.第二版》P370),對象切片是當它拷貝到一個新的對象時,會去掉原來對象的一部分,而不是像使用指針或引用那樣簡單地改變地址的內容。

        (3)test 3和test 4中,定義的基類指針和引用,故會發生多態。

 

三、晚綁定原理:虛函數表

     編譯器會對每一個包含虛函數的類(或者從包含虛函數的基類派生的類)創建一個表(VTABLE),里面存放特定類的虛函數的地址。然后編譯器秘密地放置一指針vpointer(VPTR),指向這個對象的vtable。當通過基類指針做虛函數調用時(即多態調用時),編譯器靜態地插入能取得這個VPTR並在VTABLE表中查找函數地址的代碼,這樣就能調用正確的函數並引起晚綁定的發生。

    這個具體過程, 可以參見本人的另一篇博客《C模擬實現C++的多態http://www.cnblogs.com/wly603/archive/2012/04/11/2441912.html

 

   (完)


免責聲明!

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



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