Guru of the Week 條款05:覆寫虛擬函式 (轉)
GotW #05 Overriding Virtual Functions
著者:Herb Sutter
翻譯:kingofark
[宣告]:本文內容取自網站上的Guru of the Week欄目,其著作權歸原著者本人所有。譯者kingofark在未經原著者本人同意的情況下翻譯本文。本翻譯內容僅供自學和參考用,請所有閱讀過本文的人不要擅自轉載、傳播本翻譯內容;本翻譯內容的人請在閱讀瀏覽後,立即刪除其。譯者kingofark對違反上述兩條原則的人不負任何責任。特此宣告。
Revision 1.0
Guru of the Week 條款05:覆寫虛擬:namespace prefix = o ns = "urn:schemas--com::office" />
難度:6 / 10
(虛擬函式(virtual function)真是一個招人喜歡的基本特性,對嗎?好吧,如果你能回答下面這個問題,你就會發現她們有時真是冷若冰霜、寒氣刺骨。)
[問題]
當你在一個佈滿灰塵的角落翻尋公司的存檔程式碼時,你偶然中發現了無名氏編寫的一段如下的。這位無名氏程式設計師好像曾經試圖用這個程式做試驗,看看某些C++特性的運作狀況。你知道這個程式設計師希望程式執行後列印什麼結果嗎?你知道程式執行後的實際結果嗎?
#include #include using namespace std; class Base { public: virtual void f( int ) { cout << "Base::f(int)" << endl; } virtual void f( double ) { cout << "Base::f(double)" << endl; } virtual void g( int i = 10 ) { cout << i << endl; } }; class Derived: public Base { public: void f( complex cout << "Derived::f(complex)" << endl; } void g( int i = 20 ) { cout << "Derived::g() " << i << endl; } }; void main() { Base b; Derived d; Base* pb = new Derived; b.f(1.0); d.f(1.0); pb->f(1.0); b.g(); d.g(); pb->g(); delete pb; } [解答] 首先,我們談談編碼風格方面的問題: 1. void main() 這並不是一個合法的main宣告,儘管許多都允許這種寫法。應該使用“int main()”或者“int main(int argc, char* argv[])”。 然而,其實你仍然不需要新增任何返回語句(雖然有時加上返回語句是為了形成一種向外部者報告錯誤的良好編碼風格)。實際上,如果main沒有返回語句,其效果等同於了“return 0;”。 2. delete pb; 這看起來好像既無公害又無傷大雅——當然,前提是Base的編寫者提供了一個虛擬解構函式(virtual destructor)。然而,就像我們在這個程式中所看到的那樣,在沒有虛擬解構函式(virtual destructor)的情況下透過指向基類的指標進行刪除操作,這簡直就是邪惡的犯罪,同時也是幼稚和簡單的——如此以來,崩潰就是你所能期待的最好的事情了。 [規則]:把基類的解構函式(virtual destructor)宣告為virtual。 3. Derived::f(complex Derived並沒有過載(overload)Base::f,而是隱藏了它。這個細節非常重要,因為這意味著在Derived中,Base::f(int)和Base::f(double)是不可見的!(更何況某些流行的編譯器對此種情況甚至都不給出一個警告資訊。) [規則]:當派生類中的函式與基類中的函式同名,而你又不想在派生類中隱藏這些基類函式的時候,請使用using宣告來把它們定置到可用範圍(pe)之內。 4. Derived::g(int i = 10) 除非你是故意要把別人搞糊塗,否則不要改變你所覆寫(overr)的繼承函式(inherited function)的預設引數。(一般來說,進行覆寫而不使用引數預設值並不是一個壞主意,但那是其本身的問題。)是的,這是一個合法合理的C++語句;不錯,其結果也被很好的定義了;但是,不,請不要這樣做。往下接著看,你會發現它到底是如何把人搞糊塗的。 [規則]:絕不要改變覆寫的繼承函式(overridden inherited function) 好了,讓我們不要再談那些編碼風格的瑣事了,現在我們就來看看主程式到底是不是按照那位無名氏程式設計師所期望的方式運作的。 void main() { Base b; Derived d; Base* pb = new Derived; * b.f(1.0); 沒問題,呼叫Base::f(double)。 * d.f(1.0); 這條語句呼叫Derived::f(complex 無名氏先生也許原本是想要呼叫Base::f(double),但是此處他連一個編譯錯誤資訊也得不到,因為幸運的是(?),complex (*)注:在現有的C++標準草案中,轉換建構函式(conversion constructor)不是顯式的(explicit)。 * pb->f(1.0); 有趣的事情在這裡發生了:雖然Base* pb指向一個Derived,但這個語句還是呼叫Base::f(double),因為過載解析過程(overload resolution)會以靜態型別(在這裡是指Base)完成,而不是以動態型別(在這裡是指Derived)完成。 * b.g(); 這條語句只是簡單的列印“10”,因為它引起對Base::g(int)的呼叫,其函式引數預設值為10。這沒什麼好奇怪的。 * d.g(); 這條語句列印“Derived::g()20”,因為它引起對Derived::g(int)的呼叫,其函式引數預設值為20。這也沒什麼好奇怪的。 * pb->g(); 這條語句列印“Derived::g()10”。這個結果也許會暫時死鎖你的精神剎車,導致你聲嘶力竭的進入一種精神停滯狀態——直到你認識到,編譯器的所作所為是再正常不過的了(雖然我們可憐的無名氏毫無疑問的應該被槍斃)。要記住,確定預設引數和過載一樣,是以物件的靜態型別(在這裡是指Base)完成的,因此選用了預設值10。然而,函式又恰好是virtual的,所以實際被呼叫的函式取決於物件的動態型別(在這裡是指Derived)。 最後,如果你能理解這剩下的幾個語句(儘管你會因此說:“噢嘔!”),那麼你就終於可以理解我開頭所說的“冷若冰霜、寒氣刺骨”的意思了。祝賀你! * delete pb; } 刪除操作,當然,會留下一些只被部分清除的東西,使變得不可捉摸……好好看看前面關於虛擬解構函式(virtual destructor)的敘述吧。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-990890/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Guru of the Week 條款27:轉呼叫函式 (轉)函式
- Guru of the Week #5:虛擬函式的重新定義 (轉)函式
- Guru of the Week 條款19:自動轉換 (轉)
- Guru of the Week 條款28:“Fast Pimpl”技術 (轉)AST
- Guru of the Week 條款09:記憶體管理(上篇) (轉)記憶體
- Guru of the Week 條款10:記憶體管理(下篇) (轉)記憶體
- Guru of the Week 條款24:編譯級防火牆 (轉)編譯防火牆
- Guru of the Week 條款30附錄:介面原則 (轉)
- Guru of the Week 條款13:物件導向程式設計 (轉)物件程式設計
- Guru of the Week 條款07:編譯期的依賴性 (轉)編譯
- Guru of the Week 條款11:物件等同(Object Identity)問題 (轉)物件ObjectIDE
- Guru of the Week 條款14:類之間的關係(上篇) (轉)
- Guru of the Week 條款15:類之間的關係(下篇) (轉)
- Guru of the Week 條款16:具有最大可複用性的通用Containers (轉)AI
- Guru of the Week 條款08:GotW挑戰篇——異常處理的安全性 (轉)Go
- Guru of the Week 條款23:物件的生存期(第二部分) (轉)物件
- 條款05: 瞭解c++默默編寫並呼叫哪些函式C++函式
- Guru of the Week 條款22:物件的生存期(第一部分) (轉)物件
- Guru of the Week 條款21:程式碼的複雜性(第二部分) (轉)
- C++ articles:Guru of the Week #1 (轉)C++
- Guru of the week:#18 迭代指標. (轉)指標
- 虛擬函式,虛擬函式表函式
- 虛擬函式 純虛擬函式函式
- C++中的虛擬函式與虛擬函式表 (轉)C++函式
- Guru of the Week 條款20:程式碼的複雜性(第一部分) (轉)
- Guru of the week:#17 型別對映. (轉)型別
- 條款14 基類的解構函式一定要定義為虛擬函式(From Effective C++) (轉)函式C++
- Guru of the week:#19 自動型別轉換. (轉)型別
- 介面、虛擬函式、純虛擬函式、抽象類函式抽象
- 虛擬函式實現原理(轉)函式
- C++ articles:Guru of the Week #4 -- Class Mechantics (轉)C++
- 虛擬函式函式
- c#之多型性_繼承_重寫_虛擬函式_new覆蓋整合測試C#多型繼承函式
- Guru of The week #20 程式碼的複雜性 Ⅰ. (轉)
- C++虛擬函式解析(轉載)C++函式
- [Lang] 虛擬函式函式
- C++中抽象類、虛擬函式和純虛擬函式C++抽象函式
- C++ articles:Guru of the Week #3:使用標準庫 (轉)C++