0、前言
從實現的角度來講,多態可以分爲兩類:編譯時的多態性和運行時的多態性。前者是通過靜態聯編來實現的,比如C++中通過函數的重載和運算符的重載。後者則是通過動態聯編來實現的,在C++中運行時的多態性主要是通過虛函數來實現的。
1、概念
虛函數:在基類中聲明爲virtual,並在一個或多個派生類中被重新定義的成員函數。
存在虛函數的類都有一個一維的虛函數表叫做虛表。類的對象有一個指向虛表開始的虛指針。
虛表是和類對應的,虛表指針是和對象對應的。
2、作用
虛函數的作用就是實現多態性,多態性是將接口與實現進行分離;用形象的語言來解釋就是實現以共同的方法,但因個體差異,而採用不同的策略。
3、示例程序
源程序
#include <iostream>
using namespace std;
class A
{
public:
void Print()
{
cout<<"This is A"<<endl;
}
};
class B:public A
{
public:
void Print()
{
cout<<"This is B"<<endl;
}
};
int main()
{
A a;
B b;
a.Print();
b.Print();
getchar();
return 0;
}
輸出結果:
把main函數裏的代碼改成如下這樣:
int main()
{
A a;
B b;
A *pA = &a;
A *pB = &b;
pA->Print();
pB->Print();
getchar();
return 0;
}
結果爲:
如果我們把class A改成下面這樣,即在Print函數前面加virtual修飾
class A
{
public:
virtual void Print()
{
cout<<"This is A"<<endl;
}
};
則輸出爲:
這時,class A的成員函數Print()已經變成了虛函數,而class B的Print()也成了虛函數。
也就是說:在把基類的成員函數設爲virtual後,其派生類的相應的函數也會自動變爲虛函數。對於在派生類的相應函數前是否需要用virtual關鍵字修飾,那就是你自己的問題了(語法上可加可不加,不加的話編譯器會自動加上,但爲了閱讀方便和規範性,建議加上)。
指向基類的指針在操作它的多態類對象時,會根據不同的類對象,調用其相應的函數。
4、定義虛函數的限制:
(1)非類的成員函數不能定義爲虛函數,類的成員函數中靜態成員函數和構造函數也不能定義爲虛函數,但可以將析構函數定義爲虛函數。實際上,優秀的程序員常常把基類的析構函數定義爲虛函數。因爲,將基類的析構函數定義爲虛函數後,當利用delete刪除一個指向派生類定義的對象指針時,系統會調用相應的類的析構函數。而不將析構函數定義爲虛函數時,只調用基類的析構函數。(2)只需要在聲明函數的類體中使用關鍵字“virtual”將函數聲明爲虛函數,而定義函數時不需要使用關鍵字“virtual”。
(3)當將基類中的某一成員函數聲明爲虛函數後,派生類中的同名函數(函數名相同、參數列表完全一致、返回值類型相關)自動成爲虛函數。
(4)如果聲明瞭某個成員函數爲虛函數,則在該類中不能出現和這個成員函數同名並且返回值、參數個數、類型都相同的非虛函數。
5、使用虛函數的注意事項:
(1)使用虛函數,派生類必須是基類公有派生的;(2)定義虛函數,不一定要在最高層的類中,而是看在需要動態多態性的幾個層次中的最高層類中聲明虛函數;
(3)一個虛函數無論被公有繼承了多少次,它仍然是虛函數;
(4)虛函數必須是所在類的成員函數,而不能是友元函數,也不能是靜態成員函數。因爲虛函數調用要靠特定的對象來決定該激活哪一個函數;
(5)構造函數不能是虛函數,但析構函數可以是虛函數;
6、抽象類
如果一個類中至少有一個純虛函數,那麼就稱該類爲抽象類。
注意:
(1)抽象類只能作爲其他類的基類來使用,不能建立抽象類對象;
(2)不允許從具體類中派生出抽象類(不包含純虛函數的普通類);
(3)抽象類不能用作函數的參數類型、返回類型和顯式轉化類型;
(4)如果派生類中沒有定義純虛函數的實現,而只是繼承成了基類的純虛函數。那麼該派生類仍然爲抽象類。一旦給出了對基類中虛函數的實現,那麼派生類就不是抽象類了,而是可以建立對象的具體類;
7、虛函數實現多態的原理
源代碼:
#include <iostream>
#include <string>
#include <Windows.h>
using namespace std;
class A
{
public:
virtual void fun() {cout<<"A::fun()"<<endl;}
virtual void fun2(){cout<<"A::fun2()"<<endl;}
void A_NonVirtual(){}
private:
int num_A;
};
class B:public A
{
public:
void fun() {cout<<"B::fun()"<<endl;}
void fun2(){cout<<"B::fun2()"<<endl;}
void B_NonVirtual(){}
private:
int num_B;
};
int main()
{
A a;
B b;
A *p = NULL;
p = &a;
p->fun();
p = &b;
p->fun();
system("pause");
return 0;
}
程序運行結果:
當執行p = &a;p->fun();時,將從對象a的虛函數表中查找fun函數。也即是A::fun();
當執行p = &b;p->fun();時,將從對象b的虛函數表中查找fun函數。也即是B::fun();