c++ 虚函数,纯虚函数的本质区别
轉(zhuǎn)載博客:https://mp.weixin.qq.com/s?__biz=MzAxNzYzMTU0Ng==&mid=2651289202&idx=1&sn=431ffd1fae4823366a50b68aed2838d4&chksm=80114627b766cf31f72018ef5f1fe29591e9f6f4bd72018e7aea849342ca6f0a271fb38465ae#rd
?
學(xué)習(xí)C++的多態(tài)性,你必然聽過虛函數(shù)的概念,你必然知道有關(guān)她的種種語法,但你未必了解她為什么要那樣做,未必了解她種種行為背后的所思所想。深知你不想在流于表面語法上的蜻蜓點(diǎn)水似是而非,今天我們就一起來揭開擋在你和虛函數(shù)(女神)之間的這一層窗戶紙。
?
?
首先,我們要搞清楚女神的所作所為,即語法規(guī)范。然后再去探究她背后的邏輯道理。她的語法說來也不復(fù)雜,概括起來就這么幾條:
?
在類成員方法的聲明(不是定義)語句前面加個單詞:virtual,她就會搖身一變成為虛函數(shù)。
在虛函數(shù)的聲明語句末尾中加個 =0 ,她就會搖身一變成為純虛函數(shù)。
子類可以重新定義基類的虛函數(shù),我們把這個行為稱之為復(fù)寫(override)。
不管是虛函數(shù)還是純虛函數(shù),基類都可以為提供他們的實(shí)現(xiàn)(implementation),如果有的話子類可以調(diào)用基類的這些實(shí)現(xiàn)。
子類可自主選擇是否要提供一份屬于自己的個性化虛函數(shù)實(shí)現(xiàn)。
子類必須提供一份屬于自己的個性化純虛函數(shù)實(shí)現(xiàn)。
?
?
語法都列出來了,背后的邏輯含義是什么呢?我們用一個生動的例子來說明,虛函數(shù)是如何實(shí)現(xiàn)多態(tài)性的。
?
假設(shè)我們要設(shè)計關(guān)于飛行器的類,并且提供類似加油、飛行的實(shí)現(xiàn)代碼,考慮具體情況,飛行器多種多樣,有民航客機(jī)、殲擊機(jī)、轟炸機(jī)、直升機(jī)、熱氣球、火箭甚至竄天猴、孔明燈、紙飛機(jī)!
假設(shè)我們有一位牛得一比的飛行員,他能給各式各樣的飛行器加充不同的燃料,也能駕駛各式各樣的飛行器。下面我們來看看這些類可以怎么設(shè)計。
首先,飛行器。由于我們假設(shè)所有的飛行器都有兩種行為:加油和飛行。因此我們可以將這兩種行為抽象到一個基類中,并由它來派生具體的某款飛行器。
?
這是一個描述飛行器的基類,提供了兩個基本的功能:加油和飛行
class aircraft
{
? ? void refuel(); // 加燃油,普通虛函數(shù)
? ? void fly()=0; ?// 飛行,純虛函數(shù)
};
?
這是一個普通虛函數(shù),意味著基類希望子類提供自己的個性化實(shí)現(xiàn)代碼,但基類同時也提供一個缺省的虛函數(shù)實(shí)現(xiàn)版本,在子類不復(fù)寫該虛函數(shù)的情況下作為備選方案
void aircraft::refuel()
{
? ? // 加充通用型燃油
}
?
這是一個純虛函數(shù),意味著基類強(qiáng)制子類必須提供自己的個性化版本,否則編譯將失敗。但讓人驚奇的是,C++仍然保留了基類提供該純虛函數(shù)代碼實(shí)現(xiàn)的權(quán)利,這也許是給千變?nèi)f化的實(shí)際情況留下后路
void aircraft::fly()
{
? ? // 一種不應(yīng)該被使用的缺省飛行方案
}
?
有了基類aircraft,我們就可以瀟灑地派生出各式各樣的飛行器了,比如轟炸機(jī)和直升機(jī):
轟炸機(jī)類定義,復(fù)寫了加油和飛行
class?bomber : public aircraft
{
? ? void refuel(){} // 加充轟炸機(jī)的特殊燃油!
? ? void fly(){} // 轟炸機(jī)實(shí)彈飛行!
};
?
直升機(jī)類定義,復(fù)寫了飛行代碼,但沒有復(fù)寫加油
class?copter: public aircraft
{
? ? void fly(){} // 直升機(jī)盤旋!
};
?
以上代碼可以看到,直升機(jī)類(copter)沒有自己的加油方式,直接使用了基類提供的缺省加油的方式。此時我們來定義一個能駕馭多機(jī)型的王牌飛行員類:
?
一個能王牌飛行員
class pilot
{
? ? void refuelPlane(aircraft *p);
? ? void dirvePlane(aircraft *p);
};
?
給我什么飛機(jī)我就加什么油
void pilot::refuelPlane(aircraft *p)
{
? ? p->refuel();
}
?
給我什么飛機(jī)我就怎么飛
void pilot::dirvePlane(aircraft *p)
{
? ? p->fly();
}
?
很明顯,我們接下來要給這位很浪的飛行員表演一下操縱各種飛行器的機(jī)會,我們來定義各種飛機(jī)然后丟給他去處理
?
定義兩架飛機(jī),一架轟6K,一架武直10
aircraft *H6K = new bomber;
aircraft *WZ10?= new copter;
?
來一個王牌飛行員,給H6K加油(加的是轟炸機(jī)特殊燃油),并且按照H6K的特點(diǎn)飛行
pilot Jack;
Jack.refuelPlane(H6K); ?// 加充轟炸機(jī)燃油
Jack.flyPlane(H6K); // 轟炸機(jī)實(shí)彈飛行
?
給WZ10加油(加的是基類提供的通用燃油),按照WZ10的特點(diǎn)飛行
Jack.refuelPlane(WZ10); // 加充通用型燃油
Jack.flyPlane(WZ10); // 直升機(jī)盤旋
?
上述代碼體現(xiàn)了最經(jīng)典的所謂多態(tài)的場景,給Jack不同的飛機(jī),就能表現(xiàn)不同的結(jié)果。虛函數(shù)和純虛函數(shù)都能做到這一點(diǎn),區(qū)別是,子類如果不提供虛函數(shù)的實(shí)現(xiàn),那就會自動調(diào)用基類的缺省方案。而子類如果不提供純虛函數(shù)的實(shí)現(xiàn),則編譯將會失敗。基類提供的純虛函數(shù)實(shí)現(xiàn)版本,無法通過指向子類對象的基類類型指針或引用來調(diào)用,因此不能作為子類相應(yīng)虛函數(shù)的備選方案。下面給出總結(jié)。
?
?
第一,當(dāng)基類的某個成員方法,在大多數(shù)情形下都應(yīng)該由子類提供個性化實(shí)現(xiàn),但基類也可以提供一個備選方案的時候,請將其設(shè)計為虛函數(shù)。例如飛行器的加油動作,每種不同的飛行器原則上都應(yīng)該有自己的個性化的加充然后的方式,但也不免可以有一種通用的然后和加充方式。
?
第二,當(dāng)基類的某個成員方法,必須由子類提供個性化實(shí)現(xiàn)的時候,請將其設(shè)計為純虛函數(shù)。例如飛行器的飛行動作,邏輯上每種飛行器都必須提供為其特殊設(shè)計的個性化飛行行為,而不應(yīng)該有任何一種“通用的飛行方式”。
?
第三,使用一個基類類型的指針或者引用,來指向子類對象,進(jìn)而調(diào)用經(jīng)由子類復(fù)寫了的個性化的虛函數(shù),這是C++實(shí)現(xiàn)多態(tài)性的一個最經(jīng)典的場景。
?
第四,基類提供的純虛函數(shù)的實(shí)現(xiàn)版本,并非為了多態(tài)性考慮,因?yàn)橹赶蜃宇悓ο蟮幕愔羔樅鸵脽o法調(diào)用該版本。純虛函數(shù)在基類中的實(shí)現(xiàn)跟多態(tài)性無關(guān),它只是提供了一種語法上的便利,在變化多端的應(yīng)用場景中留有后路。
?
第五,虛函數(shù)和普通的函數(shù)實(shí)際上是存儲在不同的區(qū)域的,虛函數(shù)所在的區(qū)域是可被覆蓋(也稱復(fù)寫override)的,每當(dāng)子類定義相同名稱的虛函數(shù)時就將原來基類的版本給覆蓋了,另一側(cè)面也說明了為什么基類中聲明的虛函數(shù)在后代類中不需要另加聲明一律自動為虛函數(shù),因?yàn)樗鎯Φ奈恢貌粫l(fā)生改變。而普通函數(shù)的存儲區(qū)域不會覆蓋,每個類都有自己獨(dú)立的區(qū)域互不相干。
?
最后附一幅草圖以供參考
轉(zhuǎn)載于:https://www.cnblogs.com/HPAHPA/p/8351284.html
總結(jié)
以上是生活随笔為你收集整理的c++ 虚函数,纯虚函数的本质区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 易校园app是()打造的一个校园全场景服
- 下一篇: 合并两个排序的链表递归和非递归C++实现