在一次修改代碼過程中踩的坑,下來研究了一下,發(fā)現(xiàn)C++中虛函數(shù)重載后會(huì)產(chǎn)生很多有意思的情況,在這里總結(jié)了一下。
C++中有重載(overload)和重寫(override)以及重定義這幾個(gè)概念,1 overload:指的是相同作用域中的兩個(gè)函數(shù)的函數(shù)名相同,但參數(shù)列表的個(gè)數(shù)、順序、類型不同。而override指的是子類中重新定義的父類的虛函數(shù)。2 override:overload要求兩個(gè)函數(shù)的參數(shù)列表必須不同,但是不要求這兩個(gè)函數(shù)必須是虛函數(shù)。而override要求必須是虛函數(shù)且父類的虛函數(shù)必須有virtual關(guān)鍵字,函數(shù)的參數(shù)列表和返回值也必須相同。子類中override的虛函數(shù)的訪問修飾符可以不同。3 重定義也是描述分別位于父類與子類中的同名函數(shù)的,但返回值可以不同。如果參數(shù)列表不同,這時(shí)子類中重定義的函數(shù)不論是否有virtual關(guān)鍵字,都會(huì)隱藏父類的同名函數(shù)。如果參數(shù)列表相同,但父類中的同名函數(shù)沒有virtual關(guān)鍵字修飾,此時(shí)父類中的函數(shù)仍然被隱藏。
虛函數(shù)的典型用法是:
#include <iostream>using namespace std; class Base{public: virtual void f();};void Base::f(){ cout << "Base::f()" << endl;}class Derived:public Base{public: virtual void f();};void Derived::f(){ cout << "Derived::f()" << endl;}int main() { Base *p1 = new Base; Base *p2 = new Derived; p1->f(); p2->f(); delete p1; delete p2; return 0;}程序的輸出是:
Base::f()Derived::f()那么如果虛函數(shù)之間又發(fā)生了overload,會(huì)出現(xiàn)什么情況?我們先看最簡單的情況:
#include <iostream>using namespace std; class Base{public: virtual void f(int);};void Base::f(int a){ cout << "Base::f(int) " << a << endl;}class Derived:public Base{public: virtual void f();};void Derived::f(){ cout << "Derived::f()" << endl;}int main() { Base *p1 = new Base; p1->f(1); Base *p2 = new Derived; p2->f(); delete p1; delete p2; return 0;}編譯上面的代碼,會(huì)發(fā)生如下錯(cuò)誤:
test.cpp: In function ‘int main()’:test.cpp:28:11: error: no matching function for call to ‘Base::f()’ p2->f(); ^test.cpp:10:6: note: candidate: virtual void Base::f(int) void Base::f(int a){ ^test.cpp:10:6: note: candidate expects 1 argument, 0 PRovided這就是因?yàn)楦割愔刑摵瘮?shù)的參數(shù)列表已經(jīng)發(fā)生變化,這時(shí)不論子類中重定義的函數(shù)不論是否有virtual關(guān)鍵字,都會(huì)隱藏父類的同名函數(shù)。這時(shí)子類中只是重定義了一個(gè)自己的函數(shù)virtual void f(),而并沒有override父類中對(duì)應(yīng)的虛函數(shù)。p2是一個(gè)指向Base類型的指針,根據(jù)虛函數(shù)的特性,對(duì)p2→f();的處理取決于是否override了父類的虛函數(shù),如果沒有,仍然會(huì)調(diào)用調(diào)用父類中被override的虛函數(shù),但是現(xiàn)在父類中的函數(shù)已經(jīng)成為了virtual void f(int),因此在執(zhí)行p2→f()時(shí)會(huì)由于缺少輸入?yún)?shù)而出現(xiàn)上述錯(cuò)誤。為了證明上述論斷,可以在執(zhí)行p2→f()時(shí)傳入?yún)?shù)來判斷:
#include <iostream>using namespace std; class Base{public: virtual void f(int);};void Base::f(int a){ cout << "Base::f(int) " << a << endl;}class Derived:public Base{public: virtual void f();};void Derived::f(){ cout << "Derived::f()" << endl;}int main() { Base *p1 = new Base; p1->f(1); Base *p2 = new Derived; p2->f(2); delete p1; delete p2; return 0;}這時(shí)可以通過編譯,執(zhí)行結(jié)果為
Base::f(int) 1Base::f(int) 2可以看到,子類自己定義的virtual void f()其實(shí)是父類的virtual void f(int)的一個(gè)重定義的函數(shù),這時(shí)盡管p2實(shí)際指向了一個(gè)Derived對(duì)象,但由于沒有override父類對(duì)應(yīng)的虛函數(shù),在執(zhí)行 p2→f(2)時(shí)將執(zhí)行父類的virtual void f(int)。也可以這樣修改:
#include <iostream>using namespace std; class Base{public: virtual void f(int); virtual void f();};void Base::f(int a){ cout << "Base::f(int) " << a << endl;}void Base::f(){ cout << "Base::f() " << endl;}class Derived:public Base{public: virtual void f();};void Derived::f(){ cout << "Derived::f()" << endl;}int main() { Base *p1 = new Base; p1->f(1); Base *p2 = new Derived; p2->f(); delete p1; delete p2; return 0;}因?yàn)楦割愔卸x了可被子類override的函數(shù),所以這時(shí)執(zhí)行p2→f()又會(huì)重新執(zhí)行子類的virtual void f():
Base::f(int) 1Derived::f()我們甚至還可以這樣驗(yàn)證:
#include <iostream>using namespace std; class Base {public: virtual void f(int); virtual void f();};void Base::f(int a) { cout << "Base::f(int) " << a << endl;}void Base::f() { cout << "Base::f() " << endl;}class Derived:public Base {public: virtual void f(float);};void Derived::f(float a) { cout << "Derived::f(float)" << showpoint << a << endl;}int main() { Base *p1 = new Base; p1->f(1); Base *p2 = new Derived; p2->f(**2.0**); delete p1; delete p2; return 0;}這時(shí)輸出仍然為
Base::f(int) 1Base::f(int) 2這說明如果通過指向父類的指針,調(diào)用虛函數(shù)時(shí),如果子類重定義了該虛函數(shù)(參數(shù)列表發(fā)生變化),則實(shí)際調(diào)用的仍是父類中的虛函數(shù)。上面都是通過指向父類的指針來調(diào)用虛函數(shù)的,那么如果通過指向子類的指針調(diào)用虛函數(shù)會(huì)發(fā)生什么:
#include <iostream>using namespace std; class Base {public: virtual void f(int); virtual void f();};void Base::f(int a) { cout << "Base::f(int) " << a << endl;}void Base::f() { cout << "Base::f() " << endl;}class Derived:public Base {public: virtual void f(float);};void Derived::f(float a) { cout << "Derived::f(float)" << showpoint << a << endl;}int main() { Base *p1 = new Base; p1->f(1); Derived *p2 = new Derived; p2->f(2.0); p2->f(3); delete p1; delete p2; return 0;}這時(shí)輸出就變?yōu)榱耍?/p>
Base::f(int) 1Derived::f(float)2.00000Derived::f(float)3.00000這說明,如果通過指向子類的指針調(diào)用虛函數(shù),并且子類重定義了父類的虛函數(shù),這時(shí)實(shí)際調(diào)用的就將是子類中的虛函數(shù)。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注