莎士比亞有一個(gè)關(guān)于名字的說法。"What’s in a name?" 他問道,"A rose by any other name would smell as sweet."(語出《羅密歐與朱麗葉》第二幕第二場,朱生豪先生譯為:“姓名本來是沒有意義的;我們叫做玫瑰的這一種花,要是換了個(gè)名字,他的香味還是同樣的芬芳。”梁實(shí)秋先生譯為:“姓算什么?我們所謂有玫瑰,換個(gè)名字,還是一樣的香。”——譯者注)。莎翁也寫過 "he that filches from me my good name ... makes me poor indeed."(語出《奧塞羅》第三幕第三場,朱生豪先生譯為:“可是誰偷去了我的名譽(yù),那么他雖然并不因此而富足,我卻因?yàn)槭ニ蔀槌嘭毩恕!绷簩?shí)秋先生譯為:“但是他若奪去我的名譽(yù),于他不見有利,對(duì)我卻是一件損失哩。”——譯者注)。好吧,在 C++ 中,我們?cè)撚媚姆N態(tài)度對(duì)待通過繼續(xù)得到的名字呢?
std::cin >> x; // read a new value for local x } 讀入 x 的語句指涉 local 變量 x,而不是 global 變量 x,因?yàn)閮?nèi)層作用域的名字覆蓋(“遮蔽”)外層作用域的名字。我們可以像這樣形象地表示作用域的狀況:
當(dāng)編譯器在 someFunc 的作用域中碰到名字 x 時(shí),他們巡視 local 作用域看看是否有什么東西叫這個(gè)名字。因?yàn)槟抢镉校鼈兙筒辉贆z查其它作用域。在此例中,someFunc 的 x 類型為 double,而 global x 類型為 int,但這不要緊。C++ 的 name-hiding 規(guī)則僅僅是覆蓋那個(gè)名字。而相對(duì)應(yīng)的名字的類型是否相同是無關(guān)緊要的。在此例中,一個(gè)名為 x 的 double 覆蓋了一個(gè)名為 x 的 int。
加入 inheritance 以后。我們知道當(dāng)我們?cè)谝粋€(gè) derived class member function 內(nèi)指涉位于 base class 內(nèi)的一件東西(例如,一個(gè) member function,一個(gè) typedef,或者一個(gè) data member)時(shí),編譯器能夠找到我們指涉的東西是因?yàn)?derived classes 繼續(xù)到聲明于 base classes 中的東西。實(shí)際中的運(yùn)作方法是將 derived class 的作用域嵌套在 base class 作用域之中。例如:
class Derived: public Base { public: virtual void mf1(); void mf4(); ... };
QQRead.com 推出數(shù)據(jù)恢復(fù)指南教程 數(shù)據(jù)恢復(fù)指南教程 數(shù)據(jù)恢復(fù)故障解析 常用數(shù)據(jù)恢復(fù)方案 硬盤數(shù)據(jù)恢復(fù)教程 數(shù)據(jù)保護(hù)方法 數(shù)據(jù)恢復(fù)軟件 專業(yè)數(shù)據(jù)恢復(fù)服務(wù)指南 本例中包含的既有 public 名字也有 private 名字,既有 data members 也有 member functions。member functions 既有 pure virtual 的,也有 simple (impure) virtual 的,還有 non-virtual 的。那是為了強(qiáng)調(diào)我們談?wù)摰氖虑槭顷P(guān)于名字的。例子中還可以包括其它類型的名字,例如,enums,nested classes,和 typedefs。在這里的討論中唯一重要的事情是“它們是名字”。與它們是什么東西的名字毫不相關(guān)。這個(gè)示例中使用了 single inheritance,但是一旦你理解了在 single inheritance 下會(huì)發(fā)生什么,C++ 在 multiple inheritance 下的行為就很輕易預(yù)見了。
假設(shè) mf4 在 derived class 中被實(shí)現(xiàn),其中一部分,如下:
void Derived::mf4() {
... mf2();
... } 當(dāng)編譯器看到這里對(duì)名字 mf2 的使用,它就必須斷定它指涉什么。它通過搜索名為 mf2 的某物的定義的作用域來做這件事。首先它在 local 作用域中搜索(也就是 mf4 的作用域),但是它沒有找到被稱為 mf2 的任何東西的聲明。然后它搜索它的包含作用域,也就是 class Derived 的作用域。它依然沒有找到叫做 mf2 的任何東西,所以它上移到它的上一層包含作用域,也就是 base class 的作用域。在那里它找到了名為 mf2 的東西,所以搜索停止。假如在 Base 中沒有 mf2,搜索還會(huì)繼續(xù),首先是包含 Base 的 namespace(s)(假如有的話),最后是 global 作用域。
我剛剛描述的過程雖然是正確的,但它還不是一個(gè)關(guān)于 C++ 中名字如何被找到的完整的描述。無論如何,我們的目的不是為了充分了解關(guān)于寫一個(gè)編譯器時(shí)的名字搜索問題。而是為了充分了解如何避免令人吃驚的意外,而對(duì)于這個(gè)任務(wù),我們已經(jīng)有了大量的信息。
class Derived: public Base { public: virtual void mf1(); void mf3(); void mf4(); ... };
以上代碼導(dǎo)致的行為會(huì)使每一個(gè)第一次碰到它的 C++ 程序員吃驚。基于作用域的名字覆蓋規(guī)則(scope-based name hiding rule)不會(huì)有什么變化,所以 base class 中的所有名為 mf1 和 mf3 的函數(shù)被 derived class 中的名為 mf1 和 mf3 的函數(shù)覆蓋。從名字搜索的觀點(diǎn)看,Base::mf1 和 Base::mf3 不再被 Derived 繼續(xù)!
class Derived: public Base { public: using Base::mf1; // make all things in Base named mf1 and mf3 using Base::mf3; // visible (and public) in Derived’s scope
d.mf1(); // still fine, still calls Derived::mf1 d.mf1(x); // now okay, calls Base::mf1
d.mf2(); // still fine, still calls Base::mf2
d.mf3(); // fine, calls Derived::mf3 d.mf3(x); // now okay, calls Base::mf3 這意味著假如你從一個(gè)帶有重載函數(shù)的 base class 繼續(xù),而且你只想重定義或替換它們中的一部分,你需要為每一個(gè)你不想覆蓋的名字使用 using declaration。假如你不這樣做,一些你希望繼續(xù)下來的名字會(huì)被覆蓋。
可以想象在某些時(shí)候你不希望從你的 base classes 繼續(xù)所有的函數(shù)。在 public inheritance 中,這是絕不會(huì)發(fā)生的,這還是因?yàn)椋`反了 public inheritance 在 base 和 derived classes 之間的 is-a 關(guān)系。(這就是為什么上面的 using declarations 在 derived class 的 public 部分:在 base class 中是 public 的名字在公有繼續(xù)的 derived class 中也應(yīng)該是 public。)然而,在 private inheritance中,它還是有意義的。例如,假設(shè) Derived 從 Base 私有繼續(xù),而且 Derived 只想繼續(xù)沒有參數(shù)的那個(gè) mf1 的版本。在這里,using declaration 沒有這個(gè)本事,因?yàn)橐粋€(gè) using declaration 會(huì)使得所有具有給定名字的函數(shù)在 derived class 中可見。不,這里是使用了一種不同的技術(shù)的情形,即,一個(gè)簡單的 forwarding function(轉(zhuǎn)調(diào)函數(shù)):
class Base { public: virtual void mf1() = 0; virtual void mf1(int);
... // as before };
class Derived: private Base { public: virtual void mf1() // forwarding function; implicitly { Base::mf1(); } // inline (see Item 30) ... };
...
Derived d; int x;
d.mf1(); // fine, calls Derived::mf1 d.mf1(x); // error! Base::mf1() is hidden forwarding function(轉(zhuǎn)調(diào)函數(shù))的另一個(gè)功效是用于老式的編譯器,它們(不正確地)不支持用 using declarations 將“通過繼續(xù)得到的名字”引入到 derived class 的作用域。