一道題目引發的關於c++命名域的問題--Avoid hiding inheried names
那天在一問一答上碰到一道題:
下麵程序的輸出? #include <iostream> using namespace std; static int x = 1; static int y = 2; struct A { static int x; static int y; }; int A::x = 3; int A::y = x; int main(void) { cout << A::y << endl; return 0; }先不說題目的正確答案,我當時的思路是 碰到int A::y =x 這條語句時,是將全局變量x的值賦給了結構體A中的成員變量y,所以應該是1,但是實際並不是這樣的(也許題目看起來是這樣的,但其實裏麵隱藏著一個陷阱),在得到正確答案之後,我想起來曾經看過的<<Effective C++>>一書當中,關於這部分內容的詳細解答:
在諸如以下的代碼中:
int x; //global variable int SomeFun(){ double x; std::cin>>x; }這個讀取數據的語句涉及的是local變量x,而不是global變量,因為內層作用於的名字會掩蓋(遮蓋)外圍作用於的名字。
當編譯器處於SomeFun的作用域內並遭遇x時,它首先在local作用域內尋找是否有什麼東西帶著這個名稱,如果找不到,就繼續依次向外圍作用於查找,如果找到,就不再找其他作用域。 本例中的local x是double的,但是global中x是int型的,但是不要緊,C++的名稱掩蓋規則(name-hiding rules)唯一做的一件事是:掩蓋名稱。至於類型是否相同,並不重要。於是本例中,一個double型的x掩蓋了一個int型的x。
我們再來看一個複雜點的例子:
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(); //correct! 調用dervied類的mf1 d.mf1(x); // Wrong! 因為Derived類的mf1掩蓋了Base類的mf1 d.mf2(); //correct! 調用Base::mf2 d.mf3(); //correct! 調用Derived::mf3 d.mf3(x); //Wrong!如我們所看到的,即使base class和derived class內的函數有不同的參數類型也適用,而且不論函數是virtual還是non-virtual一體適用。
這些行為的背後的基本理由是為了防止你在程序庫 或者應用框架(application framework)內建立新的derived class時附帶的從疏遠的base class繼承重載函數。而實際上如果我們正在適用public繼承而又不繼承那些重載函數,就違反了base和derived class之間的is-a 關係,而 is-a 是public繼承的基石,為此,我們可以適用using聲明式達成目標:
class Derived: public Base{ public: using Base::mf1; //讓Base class內名為mf1和mf3的所有東西在 using Base::mf3; //Derived作用域內都可見 virtual void mf1(); virtual mf3(); void mf4(); ... };
現在所有的繼承機製就可以正常運行了!
有時候我們並不想繼承base class內的所有函數,這也是可以理解的,但是要記住,在public繼承下,這是絕對不可能的,因為它違反了public繼承下derived 和base class之間的is-a 關係,(這也就是為什麼上述using聲明式唄放在derived class的public區域的原因,base class內的public名稱在publicly derived class內也應該是public)。
然而在private繼承下,它可能有意義。例如derived以private繼承自base,兒derived唯一想繼承的mf1是那個無參數版本。using聲明式在此處排不上用處了,因為它會令繼承而來的某給定名稱的所有同名函數在derived class內為可見。因此我們需要一個簡單的轉交函數(forwarding function)
class Derived: private Base{ public: virtual void mf1() {Base::mf1();} //轉交函數,暗自成為inline函數 ... }; Derived d; int x; d.mf1(); //正確,調用derived::mf1 d.mf1(x); //錯誤,因為base::mf1被掩蓋了
1. derived class內的名稱會遮掩base class內的名稱,在public繼承下沒有人希望如此
2. 為了讓掩蓋的名稱再見天日,可使用using聲明式貨轉交函數(forwarding function)
最後更新:2017-04-03 16:59:46