對于用過C++的人大體都清楚:派生類可以重實作基類中聲明為virtual的函數,并且很清楚如果想實作正确的重寫,必須滿足:派生類重實作的函數的所有屬性和基類virtual函數一緻,即函數簽名,const限制均一樣。同時為了更好地傳達代碼意圖,重實作的virtual函數最好添加備援的virtual關鍵字。
上面這些是基本要求,對于重實作,還有3個需要注意的地方:
1) 保證可替換性: 任何派生類都必須遵守基類所承諾的前條件和後條件。當然改寫後函數可以要求更少保證更多,反之不行。
2) 永遠不要修改預設參數。切記:預設參數并非函數簽名的組成部分,修改重實作函數的預設參數,并不會導緻重實作失敗,但會導緻糟糕的預設參數錯誤。對于調用者而言,同一個對象的成員函數會不加提示地根據自己通路所使用的靜态類型而接受不同的參數。換句話說,重實作函數的預設參數并不具有多态屬性。
3) 謹防基類重載(overload)函數被重實作函數所隐藏(overwrite)。(這和C++的名字查找相關)
示例1:預設參數問題
class Base {
virtual void Foo( int x = 0 );
};
class Derived : public Base {
virtual void Foo( int x = 1 ); // poor form, and surprise-inducing
Derived *pD = new Derived;
pD->Foo(); // invokes pD->Foo(1)
Base *pB = pD;
pB->Foo(); // invokes pB->Foo(0)
示例:隐藏基類重載函數
class Base{
virtual void Foo( int );
virtual void Foo( int, int );
voidFoo( int, int, int );
virtual void Foo( int ); // overrides Base::Foo(int), but hides theothers
Derived d;
d.Foo( 1 ); // ok
d.Foo( 1, 2 ); // error (oops?)
d.Foo( 1, 2, 3 ); // error (oops?)
問題産生的原因:C++編譯器的編譯過程分3步:名字查找,重載解析和通路性檢查。為加快名字查找速度,編譯器是逐漸擴大名字查找範圍的。以上述例子為例:d.Foo( 1, 2 ); 在名字查找時,編譯器首先發現派生類存在Foo函數,并停止名字查找;接着進行函數的重載解析,并發現派生類中,并未找到合适函數簽名的Foo函數,報錯!
解決方案:使用using引入基類的特定名字(見<Using聲明和指令的工作原理>)。
class Derived : public Base {// …
virtual void Foo( int ); // overrides Base::Foo(int)
using Base::Foo; // bring the other Base::Foooverloads into scope
相關文章:
<a href="http://www.cnblogs.com/zhenjing/archive/2010/12/archive/2010/10/20/1856309.html">揭秘:C++編譯器的函數編譯流程</a>
<a href="http://www.cnblogs.com/zhenjing/archive/2010/12/archive/2010/11/04/1868891.html">[C++再學習系列] Using聲明和指令的工作原理</a>
---------------------------------------------------
歡迎轉載,請注明作者和出處。
本文轉自 zhenjing 部落格園部落格,原文連結:http://www.cnblogs.com/zhenjing/archive/2010/12/02/override_overwrite_overload.html ,如需轉載請自行聯系原作者