函數重載只會發生在同一個類中,函數名相同,只能通過參數類型,參數個數或者有無const來區分。不能通過返回值類型區分,而且virtual也是可有可無的,即虛函數和普通函數在同一類中也可以構成函數重載。
基類和派生類中只能是隱藏或者覆蓋。
1)隱藏是指派生類中有函數與基類中函數同名,但是沒有構成虛函數覆蓋,就是隱藏。
隱藏的表現:若基類中函數func()被派生類中函數func()隱藏,那么無法通過派生類對象訪問基類中的func() 函數,派生類對象只能訪問到派生類中的func()函數。不過基類中的func()確實繼承到了派生類中。
2)虛函數也只是在基類和派生類中發揮多態的作用,而在同一類中虛函數也可以重載。
虛函數實現多態的條件:
a 基類中將這些成員聲明為virtual。
b 基類和派生類中的這些函數必須同名且參數類型,參數個數,返回值類型必須相同。
c 將派生類的對象賦給基類指針或者引用,實現多態。
缺少任何一條,只會是基類和派生類之間的隱藏,而不是覆蓋
3) 如何判斷基類和派生類中函數是否是隱藏?
當基類和派生類存在同名函數,不論參數類型,參數個數是否相同,派生類中的同名函數都會將基類中的同名函數隱藏掉。
a 基類和派生類都是虛函數,並且同名,但是形參類型或者形參個數不同,多態不滿足,但是構成了隱藏,只是沒有虛特性。
b 基類中不是虛函數,派生類中定義為虛函數,不構成多態,只是隱藏關系。
c 基類和派生類的兩個函數同名,都是虛函數,形參的個數和類型也都相同,但是返回值類型不同,這時編譯會報錯,因為兩個虛函數在隱藏時,返回值類型發生了沖突,因此隱藏發生錯誤。注意,如果這兩個函數不是虛函數,這不會報錯,隱藏會成功;同時,如果派生類中是虛函數,基類中不是虛函數,也不過報錯,隱藏也是成功的。但是如果基類中為虛函數,派生類中不是,也會報錯。這些說明,虛化並隱藏時,返回值類型一定要保持相同。
虛函數要求返回值類型也一樣,但是有一種情況允許虛函數返回值時本類對象的引用或者指針,也可以構成覆蓋。這個是“協變”規則,具體協變看例子:
class A: { public: virtual A* func() { cout<<"A"<<endl; return this; } }; class B:public A { public: virtual B* func() { cout<<"B"<<endl; return this; } }; int main() { A *pa=new B; B* pb=pa->func();//編譯無法通過,因為pa是A*類型指針,編譯時,對於pa->func()翻譯成調用的是A類的函數,返回值為 A*類型。而A*類型無法賦值給派生類指針 B* pb=(B*)pa->func();//正確 B* pb=(B*)(pa->func());//正確 }
A *pa=new B;對於虛函數將基類指針指向派生類對象,調用派生類的虛函數。該基類指針能解引用的內存空間是繼承到派生類中的基類的內存空間。基類指針調用派生類的虛函數,在虛函數中,this指針指向的是派生類本身,也就是在虛函數中將基類指針強制轉換成了派生類指針。其實基類指針pa和派生類中的this指針值相同,都是派生類對象的地址。
協變的存在是為了解決返回值的強制類型轉換,真正用途是,通過派生類對象調用虛函數,直接返回派生類指針。若無協變,則會返回基類指針,需要再將基類指針強制轉換成派生類指針。具體的意思看例子:
若沒有協變,那么上述的代碼中派生類中虛函數需要改成以下形式: class B :public A { public: virtual A* func() { cout<<"B"<<endl; return this;//返回值this為B*類型指針,但是因為沒有協變,返回的時候將B*類型賦值給了A*類型,然后以A*類型返回到main函數中 } }; int main() { B b; A *pa=b.func(); B *pb=dynamic<B*> (pa);//將返回的A*類型強制轉換成B*類型 }