熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> ASP編程 >> 正文

避免覆蓋通過繼承得到的名字

2022-06-13   來源: ASP編程 
莎士比亞有一個關於名字的說法Whats in a name? 他問道A rose by any other name would smell as sweet(語出《羅密歐與朱麗葉》第二幕第二場朱生豪先生譯為姓名本來是沒有意義的我們叫做玫瑰的這一種花要是換了個名字他的香味還是同樣的芬芳

  梁實秋先生譯為姓算什麼?我們所謂有玫瑰換個名字還是一樣的香——譯者注)莎翁也寫過 he that filches from me my good name makes me poor indeed(語出《奧塞羅》第三幕第三場朱生豪先生譯為可是誰偷去了我的名譽那麼他雖然並不因此而富足我卻因為失去它而成為赤貧了梁實秋先生譯為但是他若奪去我的名譽於他不見有利對我卻是一件損失哩——譯者注)好吧在 C++ 中我們該用哪種態度對待通過繼承得到的名字呢?

  事情的實質與繼承沒什麼關系它與作用域有關我們都知道它在代碼中是這樣的  

  int x; // global variable 

  void someFunc()

  {

  double x; // local variable

  std::cin >> x; // read a new value for local x

  }

  讀入 x 的語句指涉 local 變量 x而不是 global 變量 x因為內層作用域的名字覆蓋(遮蔽)外層作用域的名字我們可以像這樣形象地表示作用域的狀況  

  當編譯器在 someFunc 的作用域中遇到名字 x 時他們巡視 local 作用域看看是否有什麼東西叫這個名字因為那裡有它們就不再檢查其它作用域在此例中someFunc 的 x 類型為 double而 global x 類型為 int但這不要緊C++ 的 namehiding 規則僅僅是覆蓋那個名字而相對應的名字的類型是否相同是無關緊要的在此例中一個名為 x 的 double 覆蓋了一個名為 x 的 int

  加入 inheritance 以後我們知道當我們在一個 derived class member function 內指涉位於 base class 內的一件東西(例如一個 member function一個 typedef或者一個 data member)時編譯器能夠找到我們指涉的東西是因為 derived classes 繼承到聲明於 base classes 中的東西實際中的運作方法是將 derived class 的作用域嵌套在 base class 作用域之中例如

  class Base {

  private:

   int x;

  

  public:

   virtual void mf() = ;

   virtual void mf();

   void mf();

   

  };

  

  class Derived: public Base {

  public:

   virtual void mf();

   void mf();

   

  };

  本例中包含的既有 public 名字也有 private 名字既有 data members 也有 member functionsmember functions 既有 pure virtual 的也有 simple (impure) virtual 的還有 nonvirtual 的那是為了強調我們談論的事情是關於名字的 例子中還可以包括其它類型的名字例如enumsnested classes和 typedefs在這裡的討論中唯一重要的事情是它們是名字與它們是什麼東西的名字毫不相關這個示例中使用了 single inheritance但是一旦你理解了在 single inheritance 下會發生什麼C++ 在 multiple inheritance 下的行為就很容易預見了

  假設 mf 在 derived class 中被實現其中一部分如下  

  void Derived::mf()

  {

  

  

  mf();  

  

  }

  當編譯器看到這裡對名字 mf 的使用它就必須斷定它指涉什麼它通過搜索名為 mf 的某物的定義的作用域來做這件事首先它在 local 作用域中搜索(也就是 mf 的作用域)但是它沒有找到被稱為 mf 的任何東西的聲明然後它搜索它的包含作用域也就是 class Derived 的作用域它依然沒有找到叫做 mf 的任何東西所以它上移到它的上一層包含作用域也就是 base class 的作用域在那裡它找到了名為 mf 的東西所以搜索停止如果在 Base 中沒有 mf搜索還會繼續首先是包含 Base 的 namespace(s)(如果有的話)最後是 global 作用域

  我剛剛描述的過程雖然是正確的但它還不是一個關於 C++ 中名字如何被找到的完整的描述無論如何我們的目的不是為了充分了解關於寫一個編譯器時的名字搜索問題而是為了充分了解如何避免令人吃驚的意外而對於這個任務我們已經有了大量的信息

  再次考慮前面的示例而且這一次我們 overload mf 和 mf並且為 Derived 增加一個 mf 的版本(Derived 對 mf ——一個通過繼承得到的 nonvirtual function ——的重載使得這個設計立即變得可疑但是出於對 inheritance 之下名字可見性問題的關心我們就裝作沒看見)  

  class Base {

  private:

   int x;  

  public:

   virtual void mf() = ;

   virtual void mf(int);  

   virtual void mf();
  

   void mf();

   void mf(double);

   

  };

  

  class Derived: public Base {

  public:

   virtual void mf();

   void mf();

   void mf();

   

  };

  以上代碼導致的行為會使每一個第一次遇到它的 C++ 程序員吃驚基於作用域的名字覆蓋規則(scopebased name hiding rule)不會有什麼變化所以 base class 中的所有名為 mf 和 mf 的函數被 derived class 中的名為 mf 和 mf 的函數覆蓋 從名字搜索的觀點看Base::mf 和 Base::mf 不再被 Derived 繼承!

  

  Derived d;

  int x;

  

  

  dmf(); // fine calls Derived::mf

  dmf(x); // error! Derived::mf hides Base::mf

  dmf(); // fine calls Base::mf

  

  dmf(); // fine calls Derived::mf

  dmf(x); // error! Derived::mf hides Base::mf

  就像你看到的即使 base 和 derived classes 中的函數具有不同的參數類型它也同樣適用而且不管函數是 virtual 還是 nonvirtual它也同樣適用在本文的開始處函數 someFunc 中的 double x 覆蓋了 global 作用域中的 int x的道理相同這裡 Derived 中的函數 mf 覆蓋了具有不同類型的名為 mf 的一個 Base 函數

  這一行為背後的根本原因是為了防止當你在一個 library 或者 application framework 中創建一個新的 derived class 時偶然地發生從遙遠的 base classes 繼承 overloads 的情況不幸的是一般情況下你是需要繼承這些 overloads 的實際上如果你使用了 public inheritance 而又沒有繼承這些 overloads你就違反了base 和 derived classes 之間是 isa 關系這一 public inheritance 的基本原則在這種情況下你幾乎總是要繞過 C++ 對通過繼承得到的名字的缺省的覆蓋機制

  你可以用 using declarations 做到這一點  

  class Base {

  private:

   int x;

  

  public:

   virtual void mf() = ;

   virtual void mf(int);

  

   virtual void mf();

  

   void mf();

   void mf(double);

   

  };  

  class Derived: public Base {

  public:

   using Base::mf; // make all things in Base named mf and mf

   using Base::mf; // visible (and public) in Deriveds scope  

   virtual void mf();

   void mf();

   void mf();

   

  };

  現在 inheritance 就可以起到預期的作用  

  Derived d;
  int x;

    

  dmf(); // still fine still calls Derived::mf

  dmf(x); // now okay calls Base::mf

  dmf(); // still fine still calls Base::mf

  

  dmf(); // fine calls Derived::mf

  dmf(x); // now okay calls Base::mf

  這意味著如果你從一個帶有重載函數的 base class 繼承而且你只想重定義或替換它們中的一部分你需要為每一個你不想覆蓋的名字使用 using declaration如果你不這樣做一些你希望繼承下來的名字會被覆蓋

  可以想象在某些時候你不希望從你的 base classes 繼承所有的函數在 public inheritance 中這是絕不會發生的這還是因為它違反了 public inheritance 在 base 和 derived classes 之間的 isa 關系(這就是為什麼上面的 using declarations 在 derived class 的 public 部分在 base class 中是 public 的名字在公有繼承的 derived class 中也應該是 public)然而在 private inheritance中它還是有意義的例如假設 Derived 從 Base 私有繼承而且 Derived 只想繼承沒有參數的那個 mf 的版本在這裡using declaration 沒有這個本事因為一個 using declaration 會使得所有具有給定名字的函數在 derived class 中可見這裡是使用了一種不同的技術的情形一個簡單的 forwarding function(轉調函數)  

  class Base {

   public:

    virtual void mf() = ;

    virtual void mf(int);

   // as before

  };  

  class Derived: private Base {

   public:

    virtual void mf() // forwarding function; implicitly

    { Base::mf(); } // inline (see Item )

    

  };

  

  

  

  Derived d;

  int x;  

  dmf(); // fine calls Derived::mf

  dmf(x); // error! Base::mf() is hidden

  forwarding function(轉調函數)的另一個功效是用於老式的編譯器它們(不正確地)不支持用 using declarations 將通過繼承得到的名字引入到 derived class 的作用域

  這就是關於 inheritance 和 name hiding 的全部故事但是當 inheritance 與 templates 結合起來通過繼承得到的名字被隱藏的問題會以一種全然不同的形式呈現出來關於全部 anglebracketdemarcated(邊邊角角)的細節

  Things to Remember

  ·derived classes 中的名字覆蓋 base classes 中的名字在 public inheritance 中這從來不是想要的

  ·為了使隱藏的名字重新可見使用 using declarations 或者 forwarding functions(轉調函數)


From:http://tw.wingwit.com/Article/program/ASP/201311/21840.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.