C++虛函數的作用

https://www.jianshu.com/p/d07e0ac0ba3c?from=singlemessage

 

1、 簡單介紹

C++虛函數是定義在基類中的函數,子類必須對其進行覆蓋。在類中聲明(無函數體的形式叫做聲明)虛函數的格式如下:

virtual void display();

2、虛函數的作用

虛函數有兩大作用:

(1)定義子類對象,並調用對象中未被子類覆蓋的基類函數A。同時在該函數A中,又調用了已被子類覆蓋的基類函數B。那此時將會調用基類中的函數B,可我們本應該調用的是子類中的覆蓋函數B。虛函數即能解決這個問題。

  • 以下是沒有使用虛函數的例子:
#include<iostream>
using namespace std;
class Father                    //基類 Father
{
public:
    void display() {cout<<"Father::display()\n";}
    //在函數中調用了,子類覆蓋基類的函數display() 
    void fatherShowDisplay() {display();} 
};

class Son:public Father                 //子類Son 
{
public:
    //重寫基類中的display()函數
    void display() {cout<<"Son::display()\n";}
};

int main()
{
    Son son;        //子類對象 
    son.fatherShowDisplay();    //通過基類中未被覆蓋的函數,想調用子類中覆蓋的display函數 
}

該例子的運行結果是: Father::display()

  • 以下是使用虛函數的例子:
#include<iostream>
using namespace std;
class Father                    //基類 Father
{
public:
    virtual void display() {cout<<"Father::display()\n";}
    //在函數中調用了,子類覆蓋基類的函數display() 
    void fatherShowDisplay() {display();} 
};

class Son:public Father                 //子類Son 
{
public:
    //重寫基類中的display()函數
    void display() {cout<<"Son::display()\n";}
};

int main()
{
    Son son;        //子類對象 
    son.fatherShowDisplay();    //通過基類中未被覆蓋的函數,想調用子類中覆蓋的display函數 
}

該例子的運行結果是: Son::display()

(2)在使用指向子類對象的基類指針,並調用子類中的覆蓋函數時,如果該函數不是虛函數,那麼將調用基類中的該函數;如果該函數是虛函數,則會調用子類中的該函數。

  • 以下是沒有使用虛函數的例子:
#include<iostream>
using namespace std;
class Father                    //基類 Father
{
public:
    void display()
    {cout<<"Father::display()\n";}
};

class Son:public Father                 //子類Son 
{
public:
    void display()          //覆蓋基類中的display函數 
    {cout<<"Son::display()\n";}
};

int main()
{
    Father *fp;     //定義基類指針 
    Son son;        //子類對象 
    fp=&son;        //使基類指針指向子類對象 
    fp->display();  //通過基類指針想調用子類中覆蓋的display函數 
}

該例子的運行結果是: Father::display()
結果說明,通過指向子類對象的基類指針調用子類中的覆蓋函數是不能實現的,因此虛函數應運而生。

  • 以下是使用虛函數的例子:
#include<iostream>
using namespace std;
class Father                    //基類 Father
{
public:
    void virtual display()  //定義了虛函數
    {cout<<"Father::display()\n";}
};

class Son:public Father //子類Son 
{
public:
    void display()          //覆蓋基類中的display函數 
    {cout<<"Son::display()\n";}
};

int main()
{
    Father *fp;     //定義基類指針 
    Son son;        //子類對象 
    fp=&son;        //使基類指針指向子類對象 
    fp->display();  //通過基類指針想調用子類中覆蓋的display函數 
}

該例子的運行結果是: Son::display()

3、虛函數的實際意義

或許,很多小夥伴都會有這樣一個疑問:如果想調用子類中的覆蓋函數,直接通過子類對象,或者指向子類對象的子類指針來調用,不就沒這個煩惱了嗎?要虛函數還有什麼用呢?
其實不然,虛函數的實際意義非常之大。比如在實際開發過程中,會用到別人封裝好的框架和類庫,我們可以通過繼承其中的類,並覆蓋基類中的函數,來實現自定義的功能。
但是,有些函數是需要框架來調用,並且API需要傳入基類指針類型的參數。而使用虛函數就可以,將指向子類對象的基類指針來作爲參數傳入API,讓API能夠通過基類指針,來調用我們自定義的子類函數。這就是多態性的真正體現。

4、淺談虛函數的原理

動態連編,在運行的時候確定該調用哪個函數。由於子類中存在覆蓋函數,相當於該子類對象中有兩個函數。那麼動態連編也可以解釋爲,是在定義對象調用構造函數時,將該虛函數與該類綁定在一起。基類指針指向基類對象,那調用的肯定是基類虛函數;指向子類對象,那就調用子類虛函數。因爲在定義基類或子類對象時,就將虛函數與該類綁定了。

#include<iostream>
using namespace std;
class Father                    //基類 Father
{
public:
    void virtual display()      //定義虛函數 
    {cout<<"Father::display()\n";}
};

class Son:public Father //子類Son 
{
public:
    void display()          //覆蓋基類中的display函數 
    {cout<<"Son::display()\n";}
};

int main()
{
    Father *fp;     //定義基類指針 
    Father father;  //基類對象 
    Son son;        //子類對象 
    fp=&father;     //使基類指針指向基類對象
    fp->display(); 
    fp=&son;        //使基類指針指向子類對象 
    fp->display();  
}

運行結果爲:

Father::display()
Son::display()



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章