C++分析——多態與虛函數 virtual

在類的函數前面加上 virtual即爲虛函數,當基類的函數加上virtual時,表示該函數爲虛函數,當子類對父類進行繼承的時候,可以直接利用父類的虛函數的方法,也可以自己重新編寫該函數的方法。

C++ 虛函數的繼承關係

只有在父類的函數定義成爲了虛函數後,子類纔可以對該函數的功能進行改寫,如同同類裏的重載一樣,不過不需要有傳參類型不同的限制。沒有加虛函數符號的函數只能使用父類現有的方法。

虛函數的源碼示例

基類:Test_virtual_A

class Test_virtual_A
{
public:
    Test_virtual_A()
    {
        std::cout << __func__ << " Init " << std::endl;
    }       
    virtual ~Test_virtual_A()
    {
        std::cout << __func__ << " close" << std::endl;
    }
    void Test_func1()
    {
        std::cout << "Test_virtual_A " << __func__ << std::endl;
    }
    virtual void Test_func2()
    {
        std::cout << "Test_virtual_A " << __func__ << std::endl;
    }
    void Test_func3()
    {
        std::cout << "Test_virtual_A " << __func__ << std::endl;
    }
    void Test_func4()
    {
        std::cout << "Test_virtual_A " << __func__ << std::endl;
    }       
};

派生類Test_virtual_B

class Test_virtual_B : public Test_virtual_A
{
public:
    Test_virtual_B()
    {
        std::cout << __func__ << " Init " << std::endl;
    }       
    ~Test_virtual_B()
    {
        std::cout << __func__ << " close" << std::endl;
    }

    void Test_func1()
    {
        std::cout << "Test_virtual_B " << __func__ << std::endl;
    }
    void Test_func2()
    {
        std::cout << "Test_virtual_B " << __func__ << std::endl;
    }
    virtual void Test_func3()
    {
        std::cout << "Test_virtual_B " << __func__ << std::endl;
    }   
};

派生類Test_virtual_C

class Test_virtual_C : public Test_virtual_B
{
public:
    Test_virtual_C()
    {
        std::cout << __func__ << " Init " << std::endl;
    }       
    ~Test_virtual_C()
    {
        std::cout << __func__ << " close" << std::endl;
    }
    void Test_func1()
    {
        std::cout << "Test_virtual_C " << __func__ << std::endl;
    }
    void Test_func2()
    {
        std::cout << "Test_virtual_C " << __func__ << std::endl;
    }
};

實現函數

Test_virtual_A類自己調用

    Test_virtual_A *p = new Test_virtual_A;

    p->Test_func1();
    p->Test_func2();
    p->Test_func3();
    p->Test_func4();

    delete p;
    p = NULL;

執行結果

Test_virtual_A Init 
Test_virtual_A Test_func1
Test_virtual_A Test_func2
Test_virtual_A Test_func3
Test_virtual_A Test_func4
~Test_virtual_A close

Test_virtual_A類派生Test_virtual_B類、

    Test_virtual_A *sp = new Test_virtual_B;
    sp->Test_func1();
    sp->Test_func2();
    sp->Test_func3();
    sp->Test_func4();

    delete sp;
    sp = NULL;

執行結果

Test_virtual_A Init 
Test_virtual_B Init 
Test_virtual_A Test_func1
Test_virtual_B Test_func2   //虛函數體現
Test_virtual_A Test_func3
Test_virtual_A Test_func4
~Test_virtual_B close
~Test_virtual_A close

對於來Test_virtual_B類說,自己virtual void Test_func3()聲明爲虛函數,但在其繼承的父類Test_virtual_A中不是虛函數,因此只能使用父類Test_virtual_A的方法。

Test_virtual_A類派生Test_virtual_C類

    Test_virtual_A *rp = new Test_virtual_C;
    rp->Test_func1();
    rp->Test_func2();
    rp->Test_func3();   
    rp->Test_func4();

    delete rp;
    rp = NULL;

執行結果

Test_virtual_A Init 
Test_virtual_B Init 
Test_virtual_C Init 
Test_virtual_A Test_func1
Test_virtual_C Test_func2   //虛函數體現
Test_virtual_A Test_func3
Test_virtual_A Test_func4
~Test_virtual_C close
~Test_virtual_B close
~Test_virtual_A close

Test_virtual_B類派生Test_virtual_C類

    Test_virtual_B *tp = new Test_virtual_C;
    tp->Test_func1();
    tp->Test_func2();
    tp->Test_func3();   
    tp->Test_func4();

    delete tp;
    tp = NULL;

執行結果

Test_virtual_A Init 
Test_virtual_B Init 
Test_virtual_C Init 
Test_virtual_B Test_func1
Test_virtual_C Test_func2   //虛函數體現
Test_virtual_B Test_func3   //虛函數體現
Test_virtual_A Test_func4
~Test_virtual_C close
~Test_virtual_B close
~Test_virtual_A close

在基類Test_virtual_A中,只有函數virtual void Test_func2()被定義爲虛函數,繼承的類Test_virtual_B中,函數Test_func2沒有定義虛函數的符號,但Test_virtual_B派生出的Test_virtual_C的Test_func2函數卻實現了虛函數的特點,因此基類聲明的虛函數,即使不再使用virtual虛函數關鍵字修飾該函數,在其派生類中也是虛函數。虛函數有多重繼承的關係。 由於虛函數具有多重繼承的關係,雖然可以在派生類中間定義虛函數,但會打亂函數的關聯性,因此儘量在基類就訂好哪個函數是虛函數,爲後面的派生類進行規劃。

析構虛函數

在上一節的Test_virtual_A中,虛構函數也被定義爲虛函數virtual ~Test_virtual_A(),若沒有定義爲虛函數,那麼上面的運行就會變成以下結果。

$ ./project.o 
Test_virtual_A Init 
Test_virtual_A Test_func1
Test_virtual_A Test_func2
Test_virtual_A Test_func3
Test_virtual_A Test_func4
~Test_virtual_A close
Test_virtual_A Init 
Test_virtual_B Init 
Test_virtual_A Test_func1
Test_virtual_B Test_func2
Test_virtual_A Test_func3
Test_virtual_A Test_func4
~Test_virtual_A close          // Test_virtual_A類派生Test_virtual_B類
Test_virtual_A Init 
Test_virtual_B Init 
Test_virtual_C Init 
Test_virtual_A Test_func1
Test_virtual_C Test_func2
Test_virtual_A Test_func3
Test_virtual_A Test_func4
~Test_virtual_A close          //Test_virtual_A類派生Test_virtual_B類
Test_virtual_A Init 
Test_virtual_B Init 
Test_virtual_C Init 
Test_virtual_B Test_func1
Test_virtual_C Test_func2
Test_virtual_B Test_func3
Test_virtual_A Test_func4
~Test_virtual_B close          //Test_virtual_A類派生Test_virtual_B類
~Test_virtual_A close
$

類結束後沒有調用自身的析構函數,而是隻調用了繼承的父類的析構函數,所以需要在析構函數中加上virtual。

純虛函數

父類定義的虛函數還可以有自己的實現方法,但如果定義爲純虛函數,則將方法去掉,只定義方法的接口,方法的實現完全由所派生的子類去實現,因此其本身不能被直接調用,只能被繼承,本身作爲一個接口規範去派生子類。因此在寫純虛函數的基類的時候儘量將所有的方法都寫成純虛函數,純粹作爲一個接口的定義標準。
修改上一節的Test_virtual_A類,將Test_func1定義爲虛函數,虛函數寫法:
virtual void Test_func1() = 0;

基類函數Test_virtual_A如下所示,與上一節不同的是,這個類添加了帶參數的構造函數。

帶有純虛函數的基類Test_virtual_A

class Test_virtual_A
{
public:
    Test_virtual_A()
    {
        std::cout << __func__ << " Init " << std::endl;
    }
    Test_virtual_A(int a,int b)
    {
        std::cout << __func__ << " Init a = " << a << " b = " << b << std::endl;
    }
    virtual ~Test_virtual_A()
    {
        std::cout << __func__ << " close" << std::endl;
    }
    virtual void Test_func1() = 0;

    virtual void Test_func2()
    {
        std::cout << "Test_virtual_A " << __func__ << std::endl;
    }
    void Test_func3()
    {
        std::cout << "Test_virtual_A " << __func__ << std::endl;
    }
    void Test_func4()
    {
        std::cout << "Test_virtual_A " << __func__ << std::endl;
    }       
};

派生類Test_virtual_B

這個類有兩個繼承

class Test_virtual_B : public Test_virtual_A,public Test_inline
{
public:
    Test_virtual_B():Test_virtual_A(3,5),Test_inline(6,7)
    {
        std::cout << __func__ << " Init " << std::endl;
    }
    Test_virtual_B(int a,int b):Test_virtual_A(a,b),Test_inline(a,b)
    {
        std::cout << __func__ << " Init a = " << a << " b = " << b << std::endl;
    }
    ~Test_virtual_B()
    {
        std::cout << __func__ << " close" << std::endl;
    }

    void Test_func1()
    {
        std::cout << "Test_virtual_B " << __func__ << std::endl;
    }
    void Test_func2()
    {
        std::cout << "Test_virtual_B " << __func__ << std::endl;
    }
    virtual void Test_func3()
    {
        std::cout << "Test_virtual_B " << __func__ << std::endl;
    }   
};

派生類Test_virtual_C

與上一節不同的是,這次去掉對Test_func1的重載。

class Test_virtual_C : public Test_virtual_B
{
public:
    Test_virtual_C():Test_virtual_B(5,6)
    {
        std::cout << __func__ << " Init " << std::endl;
    }
    Test_virtual_C(int a,int b):Test_virtual_B(a,b)
    {
        std::cout << __func__ << " Init a = " << a << " b = " << b << std::endl;
    }
    ~Test_virtual_C()
    {
        std::cout << __func__ << " close" << std::endl;
    }
    void Test_func2()
    {
        std::cout << "Test_virtual_C " << __func__ << std::endl;
    }
};

實現函數

Test_virtual_A類爲指針指向Test_virtual_B類。

    Test_virtual_A *sp = new Test_virtual_B(15,36);
    sp->Test_func1();
    sp->Test_func2();
    sp->Test_func3();
    sp->Test_func4();

    delete sp;
    sp = NULL;

執行結果

$ ./project.o 
Test_virtual_A Init a = 15 b = 36
Test_virtual_B Init a = 15 b = 36
Test_virtual_B Test_func1
Test_virtual_B Test_func2
Test_virtual_A Test_func3
Test_virtual_A Test_func4
~Test_virtual_B close
~Test_virtual_A close
$

Test_virtual_A類派生Test_virtual_C類。

    Test_virtual_A *rp = new Test_virtual_C;
    rp->Test_func1();
    rp->Test_func2();
    rp->Test_func3();   
    rp->Test_func4();

    delete rp;
    rp = NULL;

執行結果

$ ./project.o 
Test_virtual_A Init a = 5 b = 6
Test_virtual_B Init a = 5 b = 6
Test_virtual_C Init 
Test_virtual_B Test_func1
Test_virtual_C Test_func2
Test_virtual_A Test_func3
Test_virtual_A Test_func4
~Test_virtual_C close
~Test_virtual_B close
~Test_virtual_A close
$

Test_virtual_B類爲指針指向Test_virtual_C類

    Test_virtual_B *tp = new Test_virtual_C(49,68);
    tp->Test_func1();
    tp->Test_func2();
    tp->Test_func3();   
    tp->Test_func4();
    cout << "Max is " << tp->Max() << endl;
cout << "Min is " << tp->Min() << endl;

    delete tp;
    tp = NULL;

執行結果

$ ./project.o 
Test_virtual_A Init a = 49 b = 68
Test_virtual_B Init a = 49 b = 68
Test_virtual_C Init a = 49 b = 68
Test_virtual_B Test_func1
Test_virtual_C Test_func2
Test_virtual_B Test_func3
Test_virtual_A Test_func4
Max is 68
Min is 49
~Test_virtual_C close
~Test_virtual_B close
~Test_virtual_A close
$

Test_virtual_C類自己創建對象

    Test_virtual_C *cp = new Test_virtual_C(57,98);
    cp->Test_func1();
    cp->Test_func2();
    cp->Test_func3();
    cp->Test_func4();
    cout << "Max is " << cp->Max() << endl;
    cout << "Min is " << cp->Min() << endl;

    delete cp;
    cp = NULL;

執行結果

$ ./project.o 
Test_virtual_A Init a = 57 b = 98
Test_virtual_B Init a = 57 b = 98
Test_virtual_C Init a = 57 b = 98
Test_virtual_B Test_func1
Test_virtual_C Test_func2
Test_virtual_B Test_func3
Test_virtual_A Test_func4
Max is 98
Min is 57
~Test_virtual_C close
~Test_virtual_B close
~Test_virtual_A close
$
發佈了52 篇原創文章 · 獲贊 34 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章