多態(tài)
多態(tài)在維基百科)中的定義如下:
在編程語言和類型論中,多態(tài)(英語:polymorphism)指為不同數(shù)據(jù)類型的實體提供統(tǒng)一的接口。多態(tài)類型(英語:polymorphic type)可以將自身所支持的操作套用到其它類型的值上。
多態(tài)按照字面意思來理解,就是多種形態(tài),當我們定義的類之間存在層次的結構時,并且類之間是通過繼承來關聯(lián)的時候,就會用到多態(tài),多態(tài)意味著調用成員函數(shù)的時候,會根據(jù)函數(shù)的調用的對象的類型來執(zhí)行不同的函數(shù)。
試想下面的情景,在一個全球性聊天軟件中,對于不同國籍的人來說,系統(tǒng)檢測到當前用戶的國籍即輸出當前用戶所說的語言。我們設計出如下代碼:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classPerson{
public:
Person(){
}
void speak(){
printf("Speakn");
}
};
classChinese:publicPerson{
public:
Chinese(){
}
void speak(){
printf("Speak Chinesen");
}
};
classEnglish:publicPerson{
public:
English(){
}
void speak(){
printf("Speak Englishn");
}
};
int main(){
ChinesePerson_Chinese;
EnglishPerson_English;
Person_Chinese.speak();
Person_English.speak();
return0;
}
運行上面的代碼,輸出的結果為:
Speak Chinese
Speak English
我們換一個思路,如果這三個人都是人那么我需要在不同國籍的人后面輸出他們所說的語言:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classPerson{
public:
intType;
public:
Person(){
Type=0;
}
void speak(){
printf("Speakn");
}
};
classChinese:publicPerson{
public:
Chinese(){
Type=1;
}
void speak(){
printf("Speak Chinesen");
}
};
classEnglish:publicPerson{
public:
English(){
Type=2;
}
void speak(){
printf("Speak Englishn");
}
};
int main(){
ChineseChinese_Person_1;
ChineseChinese_Person_2;
EnglishEnglish_Person_1;
Person* people[3];
people[0]=&Chinese_Person_1;
people[1]=&Chinese_Person_2;
people[2]=&English_Person_1;
for(R int i =0; i <3;++i){
printf("Person%d:", i +1);
if(people[i]->Type==1){
Chinese* person =(Chinese*)people[i];
person->speak();
}
elseif(people[i]->Type==2){
English* person =(English*)people[i];
person->speak();
}
}
return0;
}
嘗試編譯這段代碼輸出了同樣的結果,但是如果這樣寫代碼,寫出來的代碼將會非常冗余,我們可以嘗試在父類的函數(shù)前加上virtual關鍵字,從而實現(xiàn)相同的功能。
考慮下為什么不加關鍵字,程序的輸出結果為:
Person1:Speak
Person2:Speak
Person3:Speak
導致程序錯誤輸出的原因是,調用函數(shù)speak被編譯器設置為基類中的版本,這就是所謂的靜態(tài)多態(tài),/靜態(tài)鏈接,也就是說函數(shù)調用在程序執(zhí)行前就已經準備好了,這種情況被稱之為早綁定,因為函數(shù)在程序編譯的時候就已經被設置好了,當我們使用virtual關鍵字的時候,編譯器看的是指針的內容,而不是指針的類型,因此將子類的地址提取出來會調用各自的speak函數(shù)。
正如上所示,每一個子類都有一個函數(shù)speak獨立實現(xiàn),這就是多態(tài)的一般的使用方式,有了多態(tài)就會有多個不同的類,并且可以實現(xiàn)同一個名稱但是不同作用的函數(shù),甚至函數(shù)的參數(shù)可以完全相同。改進后的程序如下所示:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define R register
#define LL longlong
#define pi 3.141
#define INF 1400000000
usingnamespace std;
classPerson{
public:
intType;
public:
Person(){
Type=0;
}
void speak(){
printf("Speakn");
}
};
classChinese:publicPerson{
public:
Chinese(){
Type=1;
}
void speak(){
printf("Speak Chinesen");
}
};
classEnglish:publicPerson{
public:
English(){
Type=2;
}
void speak(){
printf("Speak Englishn");
}
};
int main(){
ChineseChinese_Person_1;
ChineseChinese_Person_2;
EnglishEnglish_Person_1;
Person* people[3];
people[0]=&Chinese_Person_1;
people[1]=&Chinese_Person_2;
people[2]=&English_Person_1;
for(R int i =0; i <3;++i){
printf("Person%d:", i +1);
people[i]->speak();
}
return0;
}
虛函數(shù)
虛函數(shù)是指在父類中定義的使用關鍵字virtual聲明的函數(shù),在子類中需要重新定義父類中定義的虛函數(shù)的時候程序會告訴編譯器不要靜態(tài)鏈接到這個函數(shù)。
有些時候,我們編寫程序,需要的是在程序中的任意點可以根據(jù)所調用的對象的類型來選擇我們需要調用的函數(shù),這種操作被稱為動態(tài)鏈接或者后期綁定。
純虛函數(shù)
如果我們在進行程序設計的時候,可能在父類中無法或者不需要對虛函數(shù)給出一個有意義的實現(xiàn),那么此時就需要用到純虛函數(shù),我們可以將父類中的虛函數(shù)改為如下形式:
classPerson{
public:
intType;
public:
Person(){
Type=0;
}
virtualvoid speak()=0;
};
通過上面的方法,我們告訴編譯器這個函數(shù)沒有主體,就是一個純虛函數(shù)。
動態(tài)多態(tài)的條件
1.父類中必須包含虛函數(shù),并且子類中一定要對父類中的虛函數(shù)進行重寫。
2.通過基類對象的指針或者引用進行調用虛函數(shù)
重寫
1.基類中將要被重寫的函數(shù)必須是虛函數(shù)
2.基類的派生類中的虛函數(shù)的原型必須保持一致,協(xié)變函數(shù)和析構函數(shù)(父類和子類的析構哈桑農戶不同)除外
3.訪問限定符不同
協(xié)變函數(shù) :父類(子類)的虛函數(shù)返回父類(子類)的指針或者引用
不能被定義為虛函數(shù)的子類
1.友元函數(shù),不是類的成員函數(shù)
2.全局函數(shù)
3.靜態(tài)成員函數(shù),沒有this指針
4.構造函數(shù),拷貝構造函數(shù),賦值運算符重載(賦值運算符可以作為虛函數(shù)但是不建議作為虛函數(shù))
總結一些要點
包含純虛函數(shù)的類被稱為抽象類(也成為接口類),抽象類不能實例化出對象,純虛函數(shù)在子類中重新定義之后,子類才能實例化出對象,純虛函數(shù)一定要被繼承,否則純虛函數(shù)的存在沒有任何意義,純虛函數(shù)一定沒有定義,純虛函數(shù)的存在就是用來規(guī)范子類的行為。
對于虛函數(shù)來說,父類和子類都有各自的版本,由多態(tài)方式調用的時候動態(tài)綁定。
實現(xiàn)了純虛函數(shù)的子類,這個純虛函數(shù)在子類中就變成了虛函數(shù),子類的子類可以覆蓋整個虛函數(shù),由多態(tài)的方式調用的時候進行動態(tài)綁定。
在有動態(tài)分配的堆上內存的時候,析構函數(shù)必須是虛函數(shù),但沒有必要是純虛函數(shù)。
友元函數(shù)不等于成員函數(shù),只有成員函數(shù)才可以是虛函數(shù),因此友元函數(shù)不能是虛函數(shù),但是可以通過讓友元函數(shù)調用虛擬成員函數(shù)來解決友元的虛擬問題。
析構函數(shù)應該是虛函數(shù),將調用相應的對象類型的析構函數(shù),因此如果指針指向的是子類的對象,將調用子類的洗后函數(shù),然后再自動調用父類的析構函數(shù)。
評論
查看更多