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()