覆蓋和隱藏

http://www.zahui.com/html/9/21207.htm

C++中派生類函數遮擋(Hide)基類中同名函數的問題是比較令人費解的,有必要詳細說明一下。
    看看下面一段代碼:

     class Base
     {
     public:
       virtual void f(int x) {};
     };
     class Derived : public Base
     {
     public:
       virtual void f(void* p) {};
     };
    
     Derived *pd = new Derived;
     pd->f(10);
           //編譯錯誤

    對於這樣一種情況,Scott Meyers這樣解釋(Effective C++中文第二版 第50條),Derived::f遮蔽了Base::f,即使兩者的參數類型並不相同。也就是說編譯器不知道有一個參數類型爲int的Base::f存在。
    但是下面的代碼卻可以編譯通過:

     Base *pb = new Derived;
     pb->f(10);

    Bjarne Stroustrup在ARM中對此的解釋是:假設當你調用f時,你真正想調用的時Derived版本,但你意外地使用了錯誤的參數類型(本例爲int)。更進一步假設Derived是繼承體系中的一員,而且你並不知道Derived間接繼承了某個BaseClass,後者聲明有一個虛擬函數f,需要一個int參數。這種情況下你將意外調用BaseClass::f——一個你甚至並不知道它存在的函數。這種錯誤有可能在大型繼承體系中常常發生,所以Stroustrup決定釜底抽薪(好一個釜底抽薪!)地令derived class members遮蔽同名的base class members。
    再看看以下代碼:

     class Base
     {
     public:
       virtual void f(int x) {};
       virtual void f(char* x) {};
     };

     class Derived : public Base
     {
     public:
       virtual void f(int x){};
     };

     Derived *pd = new Derived;
     pd->f((char*)2);    // 編譯錯誤
     pd->f(2);        // 通過
     Base *pb = new Derived;
     pb->f((char*)2);  // 通過
     pb->f(2);        // 通過

    根據以上的解釋,應該不難理解這樣的編譯結果。那麼我們看看靜態函數是否一樣的情況。

     class Base
     {
     public:
       static void f(int x) {};
     };
    
     class Derived : public Base
     {
     public:
       static void f(void* x) {}; //不論此定義前是否有 static,都有以下結果
     };
    
     Derived *pd = new Derived;
     pd->f(2);  // 編譯錯誤
     pd->f((void*)2);  
// 通過
     Base *pb = new Derived;
     pb->f(2);  //  通過
     pb->f((void*)2);  // 錯誤
   
    這樣我們發現對於static函數,一樣存在如上所述的遮蔽作用。C++允許派生類重新定義基類中的任何函數(private除外),這種權力是一把雙刃劍,在給予程序員能力的同時也誘導他們犯錯誤。

     class Base
     {
     public:
       static void f(int x) {};
     };
    
     class Derived : public Base
     {
     public:
       virtual void f(int x) {};
     };
    
     class Son : public Derived
     {
     public:
       virtual void f(int x){};
     };
 
     Son *ps = new Son;
     Derived *pd = ps;
     Base *pb = pd;
     ps->f(2); 
// 調用 Son::f
     pd->f(2); 
// 調用 Son::f
     pb->f(2); 
// 調用 Base::f
 
    以上這種在開發繼承層次很多的大型軟件時表現出來的精神分裂症就很難找到問題根源。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章