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