大家都覺得很自然,但是沒有注意到一個小插曲,就是這個foo()觸發的隱藏機制: 派生類的foo()由於函數名,參數與基類都相同,然而又沒有virtual修飾,因此不可避免地會觸發隱藏。
(一旦有virtual修飾就成覆蓋了!搞不清楚隱藏何時觸發的同學請百度:重載、覆蓋、隱藏的區別)
問題是,看到有同學問: 為什么此處觸發隱藏了,p和ptr在調用foo()的時候仍然調用基類的,不是被隱藏了么???
這么問的原因是,很多同學知道了有隱藏這么回事,但是不清楚隱藏觸發后會發生什么。 隱藏機制觸發之后,指針的調用取決於指針的類型。如果定義的是派生類指針,則該基類成員不可見(隱藏),但是若為基類指針,該基類成員仍然是可見的啊!因為此處的p和ptr均為基類指針,只是分別指向了基類和派生類對象,所以調用foo()的時候仍然是基類的成員。
但是如果定義個派生類指針pb,如下:
B *pb=&b;
pb->foo();
這時只會調用派生類的foo(),雖然B繼承自A,但是基類的foo()會被隱藏。
這樣看起來似乎莫名其妙,因為你想當然地認為派生類的指針肯定調用自己的成員啊,隱藏存在的意義是什么?就像此題,不用考慮它我也能做對!
但是一旦foo()里有參數的時候,你就會大吃一驚!
假設A中為void foo(float a),B中為void foo(int a):
做如下調用:
B *pb=&b;
pb->foo(3.14);
到底會調用誰?你可能會想: 首先foo()成 員不是虛函數,但是B繼承A,B中有兩個foo(),調用foo(3.14)時根據參數類型應該匹配基類的void foo(float)成員。
然而並不是!
因為觸發了隱藏機制,基類的void foo(float)會被隱藏,所以即使你調用foo(3.14)仍然只會調用派生類的void foo(int)成員。
你的驚訝正好解釋了隱藏機制存在的意義。
(PS:牛客網上C/C++專項訓練上有專門一道題考察這種情況,當時解釋里提出隱藏機制時大多數人也是一臉懵逼)
總結:
1.判斷要點:如果不是重載也不是覆蓋,派生類和基類中一旦出現同名函數,一定觸發隱藏機制(這是個簡便判斷技巧,你可以考慮除去重載和覆蓋的任何同名函數情況,一定滿足隱藏機制觸發的兩條規則)。
2.隱藏觸發的結果:指針對成員的函數調用取決於指針類型。
若本身是基類指針(不管指向基類還是派生類)則仍然調用基類成員(不會牽扯到派生類,此處是隱藏,和多態沒關系,按第1點已說明隱藏的觸發可以首先排除覆蓋,也就是多態問題);
若本身是派生類指針,這時你就會看到隱藏的威力!此時不是簡單地繼承基類的成員,然后根據參數匹配調用,而是隱藏基類成員,只會調用派生類成員。