首頁 > 軟體

C++深入探究過載重寫覆蓋的區別

2022-08-08 22:03:01

基礎類別實現

我們先實現一個基礎類別

class BaseTest
{
private:
    virtual void display() { cout << "Base display" << endl; }
    void say() { cout << "Base say()" << endl; }
public:
    virtual void func() { cout << "Base func()" << endl; }
    void exec()
    {
        display();
        say();
    }
    void f1(string a) { cout << "Base f1(string)" << endl; }
    void f1(int a) { cout << "Base f1(int)" << endl; }
    void exec2()
    {
        display();
        say();
    }
};

BaseTest類中我們實現了一個虛擬函式display和 func。

BaseTest類內部過載了f1函數,實現了兩個版本,一個引數為string一個引數為int。

同一個類中的多個同名函數叫做過載。

實現了普通函數say,exec以及exec2函數。exec和exec2函數內部呼叫了display和say函數。

子類實現

子類DeriveA繼承了基礎類別

class DeriveA : public BaseTest
{
public:
    void display() { cout << "DeriveA display()" << endl; }
    void f1(int a, int b) { cout << "DeriveA f1(int,int)" << endl; }
    void say() { cout << "DeriveA say()" << endl; }
    virtual void func() { cout << "DeriveA func()" << endl; }
    void use_base_f1(int a, int b)
    {
        BaseTest::f1(2);
        BaseTest::f1("test");
        cout << "DeriveA f1(int, int)" << endl;
    }
    void exec2()
    {
        display();
        say();
    }
};

子類DeriveA 子類重新實現了display和func函數,子類重新實現父類別的虛擬函式,叫做重寫。

同樣子類重新實現了f1和say函數,由於父類別有f1和say,所以子類重新實現覆蓋了父類別的函數,

這種普通函數被子類重寫導致父類別的函數被隱藏了,叫做覆蓋。

函數呼叫

接下來我們通過函數呼叫,看一下覆蓋,過載和重寫的區別

void derive_base_test1()
{
    DeriveA a;
    BaseTest *b = &a;
    shared_ptr<BaseTest> c = make_shared<BaseTest>();
    //輸出DeriveA func()
    b->func();
    //輸出DeriveA func()
    a.func();
    //輸出Base f1(string)
    b->f1("abc");
    //輸出Base f1(int)
    b->f1(3);
    //輸出DeriveA f1(int,int)
    a.f1(3, 5);
    a.use_base_f1(2, 4);
    cout << "========================" << endl;
    //輸出DeriveA display()
    //輸出Base say()
    b->exec();
    //輸出DeriveA display()
    //輸出Base say()
    a.exec();
    //輸出Base display
    //輸出Base say()
    c->exec();
    cout << "======================== n"
         << endl;
    //輸出 DeriveA display()
    //輸出 Base say()
    b->exec2();
    //輸出 DeriveA display()
    //輸出 DeriveA say()
    a.exec2();
    //輸出 Base display
    //輸出 Base say()
    c->exec2();
}

程式碼裡我們生成了一個DeriveA的範例a, 並將該範例返回給基礎類別BaseTest的指標b,所以:

1   b->func();會根據多型的效果呼叫子類DeriveA的func函數

2   a.func() 因為a是一個物件,所以呼叫子類DeriveA的func函數

3   b->f1(“abc”) 呼叫基礎類別BaseTest的f1,因為f1是一個普通函數

4   a.f1(3, 5) 呼叫DeriveA的f1,因為a是一個普通物件。

5   當我們想在子類裡呼叫基礎類別的f1函數,可以通過基礎類別作用域加函數名的方式,比如例子中的

a.use_base_f1就在函數內部通過BaseTest::f1呼叫了基礎類別函數f1

6   b->exec,首先b是一個指標且exec為普通函數只在基礎類別實現了,所以呼叫基礎類別的exec,

但是exec內部呼叫了虛擬函式display,此時觸發多型機制呼叫DeriveA的display函數,因為b是一個指向子類DeriveA物件的基礎類別BaseTest指標,exec內部呼叫了普通函數display,因為display不是虛擬函式,所以呼叫BaseTest的display函數

7   a.exec(); a是一個DeriveA物件,DeriveA自己沒有實現exec函數,所以呼叫基礎類別BaseTest的exec函數,exec內部呼叫display虛擬函式時由於DeriveA重寫了display函數,所以呼叫DeriveA的display函數,exec內部呼叫say函數時由於say是普通函數,所以此時呼叫的是BaseTest的say函數。

8   c->exec(); 因為c為BaseTest型別,所以呼叫的就是BaseTest的exec,內部執行的也是BaseTest的display和say。

9  b->exec2(); 因為b是一個子類BaseTest的指標,所以呼叫BaseTest的exec2函數,exec2內部呼叫display時觸發多型機制呼叫DeriveA的display,呼叫say時因為say是普通函數,所以呼叫BaseTest的say函數。

10   a.exec2(); 因為a是DeriveA類物件,且DeriveA實現了exec2,所以a呼叫DeriveA的exec2,這樣exec2內部呼叫的都是DeriveA的say和display

11   c->exec2(); c為BaseTest類物件,所以呼叫BaseTest類的exec2以及display和say函數。

總結

考察一個函數是被子類還是基礎類別呼叫時應該分以下幾種情況

1  該函數是虛擬函式並且被子類重寫,如果是基礎類別指標指向子類物件,呼叫該函數則引發多型機制,呼叫子類的虛擬函式

2   如果該函數時虛擬函式並且沒有被重寫,那麼無論呼叫的物件是基礎類別指標還是子類物件,還是基礎類別物件,

還是子類指標都是呼叫基礎類別的這個虛擬函式

3   如果該函數不是虛擬函式,如果該函數被子類覆蓋(子類重新定義了同名函數),那麼呼叫規則就是子類呼叫子類的該函數,

基礎類別呼叫該基礎類別的函數。

4   如果該函數不是虛擬函式,並且子類沒有定義同名函數(沒有覆蓋基礎類別同名函數),那麼無論是子類還是基礎類別的指標或者物件,

統一呼叫的是基礎類別的函數。

5   如果第4點裡基礎類別的函數(沒有被子類覆蓋),但是內部呼叫了基礎類別的虛擬函式,並且該虛擬函式被子類重寫,這時內部這個虛擬函式呼叫規則

就要看呼叫物件的實際型別,符合1的呼叫標準,多型就走子類,不是多型就走基礎類別(此時符合2標準)

6  如果第3點裡基礎類別的函數(被子類覆蓋),但是內部呼叫了基礎類別的虛擬函式,並且該虛擬函式被子類重寫,這時內部這個虛擬函式呼叫規則

就要看呼叫物件的實際型別,符合1的呼叫標準,多型就走子類,不是多型就走基礎類別(此時符合2標準)

資源連結

本文模擬實現了vector的功能。

視訊連結

原始碼連結 

到此這篇關於C++深入探究過載重寫覆蓋的區別的文章就介紹到這了,更多相關C++過載重寫覆蓋內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com