一个例子彻底搞懂C++的虚函数和纯虚函数
學(xué)習(xí)C++的多態(tài)性,你必然聽過虛函數(shù)的概念,你必然知道有關(guān)她的種種語法,但你未必了解她為什么要那樣做,未必了解她種種行為背后的所思所想。深知你不想在流于表面語法上的蜻蜓點(diǎn)水似是而非,今天我們就一起來揭開擋在你和虛函數(shù)(女神)之間的這一層窗戶紙。
首先,我們要搞清楚女神的所作所為,即語法規(guī)范。然后再去探究她背后的邏輯道理。她的語法說來也不復(fù)雜,概括起來就這么幾條:
1.在類成員方法的聲明(不是定義)語句前面加個單詞:virtual,她就會搖身一變成為虛函數(shù);
2.在虛函數(shù)的聲明語句末尾中加個 =0 ,她就會搖身一變成為純虛函數(shù);
3.子類可以重新定義基類的虛函數(shù),我們把這個行為稱之為復(fù)寫(override);
4.不管是虛函數(shù)還是純虛函數(shù),基類都可以為他們提供實(shí)現(xiàn)(implementation),如果有的話子類可以調(diào)用基類的這些實(shí)現(xiàn);
5.子類可自主選擇是否要提供一份屬于自己的個性化虛函數(shù)實(shí)現(xiàn);
6.子類必須提供一份屬于自己的個性化純虛函數(shù)實(shí)現(xiàn)。
語法都列出來了,背后的邏輯含義是什么呢?我們用一個生動的例子來說明,虛函數(shù)是如何實(shí)現(xiàn)多態(tài)性的。
假設(shè)我們要設(shè)計(jì)關(guān)于飛行器的類,并且提供類似加油、飛行的實(shí)現(xiàn)代碼,考慮具體情況,飛行器多種多樣,有民航客機(jī)、殲擊機(jī)、轟炸機(jī)、直升機(jī)、熱氣球、火箭甚至竄天猴、孔明燈、紙飛機(jī)!
假設(shè)我們有一位牛得一比的飛行員,他能給各式各樣的飛行器加充不同的燃料,也能駕駛各式各樣的飛行器。下面我們來看看這些類可以怎么設(shè)計(jì)。
首先,飛行器。由于我們假設(shè)所有的飛行器都有兩種行為:加油和飛行。因此我們可以將這兩種行為抽象到一個基類中,并由它來派生具體的某款飛行器。
1 // 這是一個描述飛行器的基類,提供了兩個基本的功能:加油和飛行。 2 class aircraft { 3 virtual void refuel(); // 加燃油,普通虛函數(shù) 4 virtual void fly()=0; // 飛行,純虛函數(shù) 5 }; 1 // 這是一個普通虛函數(shù),意味著基類希望子類提供自己的個性化實(shí)現(xiàn)代碼,但基類 2 // 同時也提供一個缺省的虛函數(shù)實(shí)現(xiàn)版本,在子類不復(fù)寫該虛函數(shù)的情況下作為備選方案。 3 void aircraft::refuel() { 4 // 加通用型燃油 5 std::cout << "加通用燃油" << std::endl; 6 } 1 // 這是一個純虛函數(shù),意味著基類強(qiáng)制子類必須提供自己的個性化版本,否則編譯將失敗。 2 void aircraft::fly() { 3 // 一種不應(yīng)該被使用的缺省飛行方案 4 std::cout << "一種不應(yīng)該被使用的缺省飛行方案" << std::endl; 5 }但讓人驚奇的是,C++仍然保留了基類提供該純虛函數(shù)代碼實(shí)現(xiàn)的權(quán)利,這也許是給千變?nèi)f化的實(shí)際情況留下后路。
有了基類aircraft,我們就可以瀟灑地派生出各式各樣的飛行器了,比如轟炸機(jī)和直升機(jī):
1 // 轟炸機(jī)類定義,復(fù)寫了加油和飛行 2 class bomber : public aircraft { 3 void refuel() {} // 加轟炸機(jī)的特殊燃油 4 void fly() {} // 轟炸機(jī)實(shí)彈飛行 5 }; 6 7 // 直升機(jī)類定義,復(fù)寫了飛行代碼,但沒有復(fù)寫加油 8 class copter : public aircraft { 9 void fly() {} // 直升機(jī)盤旋 10 };以上代碼可以看到,直升機(jī)類(copter)沒有自己的加油方式,直接使用了基類提供的缺省加油的方式。此時我們來定義一個能駕馭多機(jī)型的王牌飛行員類:
1 // 一個能駕駛各種飛行器的王牌飛行員 2 class pilot { 3 void refuelPlane(aircraft *p); 4 void dirvePlane(aircraft *p); 5 }; 1 // 給我什么飛機(jī)我就加什么油 2 void pilot::refuelPlane(aircraft *p) { 3 p->refuel(); 4 } 5 6 // 給我什么飛機(jī)我就怎么飛 7 void pilot::dirvePlane(aircraft *p) { 8 p->fly(); 9 }很明顯,我們接下來要給這位很浪的飛行員表演一下操縱各種飛行器的機(jī)會,我們來定義各種飛機(jī)然后丟給他去處理。
1 // 定義兩架飛機(jī),一架轟6K,一架武直10 2 aircraft *H6K = new bomber; 3 aircraft *WZ10 = new copter; 1 // 來一個王牌飛行員,給H6K加油(加的是轟炸機(jī)特殊燃油),并且按照H6K的特點(diǎn)飛行 2 pilot Jack; 3 Jack.refuelPlane(H6K); // 加轟炸機(jī)燃油 4 Jack.flyPlane(H6K); // 轟炸機(jī)實(shí)彈飛行 5 6 // 給WZ10加油(加的是基類提供的通用燃油),按照WZ10的特點(diǎn)飛行 7 Jack.refuelPlane(WZ10); // 加通用型燃油 8 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é):
1.當(dāng)基類的某個成員方法,在大多數(shù)情形下都應(yīng)該由子類提供個性化實(shí)現(xiàn),但基類也可以提供一個備選方案的時候,請將其設(shè)計(jì)為虛函數(shù)。例如飛行器的加油動作,每種不同的飛行器原則上都應(yīng)該有自己的個性化的加充燃油的方式,但也不免可以有一種通用的燃油加充方式;
2.當(dāng)基類的某個成員方法,必須由子類提供個性化實(shí)現(xiàn)的時候,請將其設(shè)計(jì)為純虛函數(shù)。例如飛行器的飛行動作,邏輯上每種飛行器都必須提供為其特殊設(shè)計(jì)的個性化飛行行為,而不應(yīng)該有任何一種"通用的飛行方式";
3.使用一個基類類型的指針或者引用,來指向子類對象,進(jìn)而調(diào)用經(jīng)由子類復(fù)寫了的個性化的虛函數(shù),這是C++實(shí)現(xiàn)多態(tài)性的一個最經(jīng)典的場景;
4.基類提供的純虛函數(shù)的實(shí)現(xiàn)版本,并非為了多態(tài)性考慮,因?yàn)橹赶蜃宇悓ο蟮幕愔羔樅鸵脽o法調(diào)用該版本。純虛函數(shù)在基類中的實(shí)現(xiàn)跟多態(tài)性無關(guān),它只是提供了一種語法上的便利,在變化多端的應(yīng)用場景中留有后路;
5.虛函數(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://blog.csdn.net/vincent040/article/details/78848322,并對代碼做了小幅修正,在此感謝原作者。
轉(zhuǎn)載于:https://www.cnblogs.com/chwei2ch/p/10628608.html
總結(jié)
以上是生活随笔為你收集整理的一个例子彻底搞懂C++的虚函数和纯虚函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 彼岸世界剧情介绍
- 下一篇: 什么样的行为会构成经济犯罪,举几个例子