您好,登錄后才能下訂單哦!
《Effective C++》
條款33:避免遮掩繼承而來的名稱
遮掩行為與作用域有關。例子如下:
int x;//global變量 void someFun() { double x;//local 變量 std::cin >> x;//讀一個新值賦予local變量x }
這個讀取數據的語句指涉的是local變量x,而不是global變量x,因為內層作用域的名稱會遮掩外圍作用域的名稱。
例如:
class Base { private: int x; public: virtual void mf1() = 0; virtual void mf2(); void mf3(); ... }; class Derived : public Base { public: virtual void mf1(); void mf4(); ... };
此例內含一組混含了public和private名稱,以及一組成員變量和成員函數名稱。這些成員函數包括pure virtual、impure virtual和non-virtual三種,這是為了強調我們談的是名稱,和其他無關。
假設derived class內的mf4的實現碼如下:
void Derived::mf4() { ... mf2(); ... }
當編譯器看到這里使用了mf2,必須估算它指涉什么東西。編譯器的做法是查找各作用域,看看有沒有某個名為mf2的申明式。首先查找local作用域(也就是mf4覆蓋的作用域),在那兒沒有找到任何東西名為mf2.于是查找其他外圍作用域,也就是class Derived覆蓋的作用域。還是沒找到任何東西名為mf2,于是再往外圍移動,本例為base class。在那兒編譯器找到一個名為mf2的東西了,于是停止查找。如果Base內還是沒有mf2,查找動作便繼續下去,首先找內含Base的那個namespace的作用域,最后往global作用域找去。
class Base { private: int x; public: virtual void mf1() = 0; virtual void mf1(int); virtual void mf2(); void mf3(); void mf3(double); ... }; class Derived : public Base { public: virtual void mf1(); void mf3(); void mf4(); ... }; Derived d; int x; ... d.mf1();//OK d.mf1(x);//error!!! Derived::mf1遮掩了Base::mf1 d.md2();//OK d.mf3();//OK d.mf3(x);//Error!!! Derived::mf3遮掩了Base::mf3
這些行為背后的基本理由是為了防止你的程序庫或應用框架內建立新的derived class 時附帶從疏遠的base class繼承重載函數。不幸的是你通常會想繼承重載函數。實際上如果你正在使用public 繼承而又不繼承那些重載函數,就是違反base和derived class之間的is-a關系,而is-a是public 繼承的基石。因此你總會想要推翻C++對“繼承而來的名稱”的缺省遮掩行為。可以使用using聲明表達式達成這一目標:
class Base { private: int x; public: virtual void mf1() = 0; virtual void mf1(int); virtual void mf2(); void mf3(); void mf3(double); ... }; class Derived : public Base { public: using Base::mf1;//讓Base class內名為mf1和mf3的所有東西在Derived作用域都可見 using Base::mf3; virtual void mf1(); void mf3(); void mf4(); ... }; Derived d; int x; ... d.mf1();//OK d.mf1(x);//OK,調用Base::mf1 d.md2();//OK d.mf3();//OK d.mf3(x);//OK,調用Base::mf3
也就是說如果你繼承base class并加上重載函數,而你又希望重新定義或覆寫其中一部分,那么你必須為那些原本會被遮掩的每個名稱引入一個using聲明式,否則某些你希望繼承的名稱會被遮掩。
有時你并不想繼承base class的所有函數,這是可以理解的。在public繼承下,這絕對不可能發生。但是在private形式繼承Base,而Derived唯一想繼承的mf1是那個無參數版本。using聲明式在這里派不上用場。一個簡單的轉交函數(forwarding functions)
class Base { public: virtual void mf1() = 0; virtual void mf1(int); ... }; class Derived : private Base { public: virtual void mf1()//轉交函數暗自為inline { Base::mf1(); } ... }; Derived d; int x; d.mf1();//OK,調用的是Derived::mf1 d.mf1(x);//Error!Base::mf1()被遮掩
總結:
derived classes內的名稱會遮掩base classes內的名稱。在public繼承下從來沒有人希望如此。
為了讓被遮掩的名稱再見天日,可使用using聲明式或轉交函數(forwarding functions)。
2016-11-09 12:56:26
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。