国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

C++箴言:避免覆蓋通過繼承得到的名字

2019-11-17 05:13:29
字體:
供稿:網(wǎng)友

  莎士比亞有一個(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ù)得到的名字呢?

  事情的實(shí)質(zhì)與繼續(xù)沒什么關(guān)系。它與作用域有關(guān)。我們都知道它在代碼中是這樣的,

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,因?yàn)閮?nèi)層作用域的名字覆蓋(“遮蔽”)外層作用域的名字。我們可以像這樣形象地表示作用域的狀況:

C++箴言:避免覆蓋通過繼續(xù)得到的名字(圖一)

  當(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 Base {
PRivate:
 int x;

public:
 virtual void mf1() = 0;
 virtual void mf2();
 void mf3();

 ...
};

class Derived: public Base {
public:
 virtual void mf1();
 void mf4();
  ...
};
C++箴言:避免覆蓋通過繼續(xù)得到的名字(圖二)

  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)有了大量的信息。

  再次考慮前面的示例,而且這一次我們 overload mf1 和 mf3,并且為 Derived 增加一個(gè) mf3 的版本。(Derived 對(duì) mf3 ——一個(gè)通過繼續(xù)得到的 non-virtual function ——的重載,使得這個(gè)設(shè)計(jì)立即變得可疑,但是出于對(duì) inheritance 之下名字可見性問題的關(guān)心,我們就裝作沒看見。)

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();
 ...
};
C++箴言:避免覆蓋通過繼續(xù)得到的名字(圖三)

  以上代碼導(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ù)!

Derived d;
int x;

...
d.mf1(); // fine, calls Derived::mf1
d.mf1(x); // error! Derived::mf1 hides Base::mf1
d.mf2(); // fine, calls Base::mf2

d.mf3(); // fine, calls Derived::mf3
d.mf3(x); // error! Derived::mf3 hides Base::mf3

  就像你看到的,即使 base 和 derived classes 中的函數(shù)具有不同的參數(shù)類型,它也同樣適用,而且不管函數(shù)是 virtual 還是 non-virtual,它也同樣適用。與“在本文的開始處,函數(shù) someFunc 中的 double x 覆蓋了 global 作用域中的 int x”的道理相同,這里 Derived 中的函數(shù) mf3 覆蓋了具有不同類型的名為 mf3 的一個(gè) Base 函數(shù)。

  這一行為背后的根本原因是為了防止“當(dāng)你在一個(gè) library 或者 application framework 中創(chuàng)建一個(gè)新的 derived class 時(shí),偶然地發(fā)生從遙遠(yuǎn)的 base classes 繼續(xù) overloads 的情況”。不幸的是,一般情況下你是需要繼續(xù)這些 overloads 的。實(shí)際上,假如你使用了 public inheritance 而又沒有繼續(xù)這些 overloads,你就違反了“base 和 derived classes 之間是 is-a 關(guān)系”這一 public inheritance 的基本原則。在這種情況下,你幾乎總是要繞過 C++ 對(duì)“通過繼續(xù)得到的名字”的缺省的覆蓋機(jī)制。

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ù)指南   你可以用 using declarations 做到這一點(diǎn):

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; // make all things in Base named mf1 and mf3
 using Base::mf3; // visible (and public) in Derived’s scope

 virtual void mf1();
 void mf3();
 void mf4();
 ...
};
C++箴言:避免覆蓋通過繼續(xù)得到的名字(圖四)

  現(xiàn)在 inheritance 就可以起到預(yù)期的作用:

Derived d;
int x;

...

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 的作用域。

  這就是關(guān)于 inheritance 和 name hiding 的全部故事,但是當(dāng) inheritance 與 templates 結(jié)合起來,“通過繼續(xù)得到的名字被隱藏”的問題會(huì)以一種全然不同的形式呈現(xiàn)出來。關(guān)于全部 angle-bracket-demarcated(邊邊角角)的細(xì)節(jié)。

  Things to Remember

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

  ·為了使隱藏的名字重新可見,使用 using declarations 或者 forwarding functions(轉(zhuǎn)調(diào)函數(shù))。


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 常州市| 安泽县| 杨浦区| 东宁县| 漯河市| 新建县| 老河口市| 曲阜市| 姜堰市| 康马县| 英吉沙县| 麻栗坡县| 阜新市| 蓬安县| 河南省| 阜平县| 长顺县| 宿迁市| 曲靖市| 曲阳县| 松原市| 贵港市| 临汾市| 平湖市| 乌兰察布市| 无锡市| 阿拉善盟| 宜兴市| 永靖县| 灵台县| 盈江县| 华宁县| 崇阳县| 福安市| 冀州市| 鄯善县| 沿河| 邮箱| 邢台县| 赤峰市| 乾安县|