條款36:絕對不要重新定義繼承而來的non-virtual函數

條款36:絕對不要重新定義繼承而來的non-virtual函數

內容:
    我們來看個例子,class B爲基類,class D爲public繼承自B的子類:
    class B{
    public:
        void func(){...}       
    };
    class D:public B{...}
    接下來我們面對以下行爲:
    D dObject;
    B* basePtr = &dObject;
    D* dOjbectPtr = &dObject;
    看下面這兩種調用方式:
    basePtr->func();
    dOjbectPtr->func();
    兩個指針指向同一個對象,調用的同一個方法,得到的結果當然是一樣的了。現在我要在子類D中重新定義func()函數,再次我們測試一下上面的兩種調用方式,結果還是一樣嗎?這次的結果卻出乎我們的意料,結果竟然不一樣了:basePtr->func()調用的函數版本依然不變,而dOjbectPtr->func()居然調用了D::func版本。你這裏可別犯迷糊,出現這樣的結果的原因也是很顯然--函數名"遮掩"現象(條款33,還記得嗎?呵呵)。這樣看來重新定義了non-virtual函數導致了D對象的"精神分裂",同一個對象調用同一個方法,產生不同的結果,這結果卻依賴對該對象的reference類型或指向該對象的指針類型.
    如果這樣的表述形式不能使你同意本條款的話,我也很樂意從理論層次對其進行解釋:條款32中我們提到對於D的繼承體系中,適用於B對象的每一件事情同樣也適用與D對象,因爲D與B是is-a的關係。我們將視線轉回到這裏如果D真的必須實現與B不同的func版本,而每一個B對象都必須使用自己的func版本,那麼"每一個D都是B"就不爲真,那麼D就不應該public繼承B.另一方面,條款34中我們提到non-virtual函數是"不變性凌駕與特異性之上",如果D真的需要實現不同的func版本的話,那麼func就無法反映其"不變性凌駕與特異性之上"特性,既然這樣func就應該聲明爲virtual。最後如果每一個D都是一個B,且如果func真的爲B反映出"不變性凌駕與特異性之上"的特性,那麼就不需要D重新定義func,而且它也不應該嘗試這麼做。
    不論哪一種觀點,結論都相同:任何情況下都不該重新定義一個繼承而來的non-virtual函數.
    請記住:
    ■ 絕對不要重新定義繼承而來的non-virtual函數.

發佈了62 篇原創文章 · 獲贊 4 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章