青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

woaidongmao

文章均收錄自他人博客,但不喜標(biāo)題前加-[轉(zhuǎn)貼],因其丑陋,見諒!~
隨筆 - 1469, 文章 - 0, 評論 - 661, 引用 - 0
數(shù)據(jù)加載中……

當(dāng)“友元”遇到“虛函數(shù)”

前些天d2school QQ群里有網(wǎng)友在討論這一內(nèi)容,我試圖做個整理。

幾點基本知識:

1、如果類A是類B的友元,則類A(的成員函數(shù))可以直接訪問類B的私有成員。

2、友元不能繼承。也就是說,類A是類B的友元,類D是類B的派生類,則類A并不會直接是類D的友元。通俗一點,父親的朋友,并不天生就是兒子的朋友。

3、虛函數(shù)的基本知識就不說了。

來看下面的幾段代碼:

Code:

1.      class A; 

2.       

3.      class

4.     

5.      private

6.          virtual void output() 

7.          { 

8.              cout << "B::output" << endl; 

9.          } 

10.       

11.      friend class A;      

12.  }; 

13.   

14.  class D : public

15. 

16.  private

17.      virtual void output() 

18.      { 

19.          cout << "D::output" << endl; 

20.      }     

21.  }; 

A B 的友元類, 而DB的派生類。 所以,若想在A中直接訪問D的代碼,則編譯不過:

Code:

1.      class

2.     

3.      public:     

4.          void test() 

5.          { 

6.              D d; 

7.              d.output(); //編譯出錯 

8.          } 

9.      }; 

這一點大家都沒覺得有問題,畢竟書上寫得都明白直觀:父類的友元,并不會因為繼承,而成為派生類的友元。

但若代碼改成這樣,編譯器似乎就被欺騙了:

Code:

1.      class

2.     

3.      public:     

4.          void test() 

5.          { 

6.              D d; 

7.              B* pb = &d;    

8.              pb->output(); //編譯通過 

9.          } 

10.  }; 

沒錯,很多人會認(rèn)為這種代碼,就算能通過編譯器,也很可能是一種不好的代碼,因為它怎么看都像是在欺騙編譯器。是這樣嗎?先不討論。先問一個問題:上面的08行代碼,output調(diào)用的是B類的那個output,還是D類的那個呢?

回答正確并不難——既然會認(rèn)定這段代碼帶有欺騙性質(zhì),而且又注意到output是一個虛函數(shù)的話——就能能正確地解答: 調(diào)用的是D類的。A明明只是B的友元,但卻通過一個簡單的類型轉(zhuǎn)換,就訪問了D類的那個私有函數(shù),所以會覺得這是一種欺騙

如果這是一種欺騙,那我們先來回答這個騙局為什么能成立:因為友元的判斷(resolve),在編譯期決定;而虛函數(shù)在運(yùn)行期去resolve。在編譯08行代碼時,編譯器看到*pb的類型是B,而AB的友元,所以允許它調(diào)用output(它認(rèn)為是B::output);而在運(yùn)行時,由于output是虛函數(shù),所以最終被決定到D::output頭上。

沒時間細(xì)查手頭的《The Design and Evolution fo C++》,但不管這樣的設(shè)計是有意為之,還是無奈之舉,或者僅僅是C++眾多的特性正交現(xiàn)象之一,我個人覺得這個特性其實正是我們想要的。 

一、首先要理解為什么派生類不應(yīng)該繼承基類的"友元",這一點很多C++的書講到了

二、其次要理解它的語法機(jī)制:前面講的,一個編譯期屬性與一個運(yùn)行期屬性相遇了……

三、要理解如果想關(guān)掉這一類欺騙,其實做不到。且來看看,該法之一是在編譯期,也檢查實際調(diào)用對象的類型,前面的示例代碼不難做到這一點,但下面的代碼中,pb來自一個形參:

Code:

1.      A::test_2(B* pb) 

2.     

3.          pb->output(); //很難在編譯期反查出pb的實際類型。 

4.                       //因為調(diào)用test_2()的代碼,可能無處不在,甚至可能在未來的代碼 

5.     

四、要理解有時候,人們其實就是在故意做這種事,最典型的做法,就是通過非虛函數(shù)調(diào)用虛函數(shù): 

Code:

1.      class

2.     

3.      public

4.          void Action() 

5.          { 

6.                   this->DoAction(); 

7.          } 

8.      private

9.            virtual void DoAction() = 0; //一個私有的純虛函數(shù) 

10.   

11.      //  friend class A;

12.  }; 

 任何一個合格的C++程序員,都應(yīng)該學(xué)會這種作法。DoAction是一個純虛函數(shù),這里我們更決絕一點,干脆讓它是私有的,這就是逼著派生類自己去實現(xiàn)一個完全自我的DoAction(),假設(shè)有個class D : public B,并且聽話地實現(xiàn)了DoAction。具體D的定義,為節(jié)省點篇幅,不寫了。

 注意到第11行的注釋, class A 現(xiàn)在已經(jīng)不是 B 的友員了,但不要緊,我們只是想在新版的類A中,調(diào)用Action函數(shù),而它是public的,所以這里不需要友元來攪和。

Code:

1.      class

2.     

3.            void test() 

4.            { 

5.                     B* pb = new D; //pb 實際指向一個D對象。  

6.                     pb->Action(); // Action  公開的,所以可以調(diào)用 

7.            } 

8.      }; 

pb 調(diào)用了非虛的B::Action函數(shù),但在Action內(nèi)調(diào)用了虛函數(shù)DoAction,再由于pb實際指向的是D對象,所以最終調(diào)用的是D::DoAction()——這了無新意對不對?只要學(xué)過一點C++的多態(tài),都會懂這一點。沒錯,它太司空見慣了,基本上所有C++程序員每天都會在寫類似的代碼——這就是我想說的,有時候,看起來在調(diào)用基類的代碼,但實際上在調(diào)用派生類的代碼。假設(shè)我們修改了語法規(guī)則,逼著虛函數(shù)在遇上友元之后失效,那就是逼著程序員不去用friend,去將更多本來應(yīng)該是private的成員,用各種該法寫成public的。

五、接著,是一個看起來很簡單,但卻被很多人誤解的概念:友元是破壞了封裝了嗎?錯,友元其實是促進(jìn)了更好的封裝。它基于這樣的需求:有一個類,它有那么幾個成員(數(shù)據(jù)或函數(shù)),它只能對個別的其它類公開,這時,你可以考慮使用友元這項技術(shù)。如果不用,會有很多人就把那些成員直接修改為public,結(jié)果:原本應(yīng)只對個別類開放的屬性,變成對所有類開放了。俗氣一點,法律規(guī)定老婆可以在私有場合下看老公的屁屁,如果法律強(qiáng)制規(guī)定不允許有這種例外,那很可能會有一些哥們,直接把屁股public出來就上街了——你若問他為什么,他也很無辜:我不過是想讓我老婆方便一些。

六、讀了第五點,對OO有一套的C++程序員要筆試/鄙視我了,好,好,我知道既使不用friend的屬性,也可以美滿地實現(xiàn)前述的,類似老婆看老公屁股的問題——我是說,通過OO技術(shù),避開需要只對部分類開放權(quán)限的需求,而轉(zhuǎn)化為第三方(比如某個接口及它的實現(xiàn)類)中——不管如何,你必須承認(rèn)友元沒有破壞封裝性,因為其它解決這一問題的的,似乎更純粹的OO技術(shù),它們美好的地方是在于更細(xì)的類顆粒,以及更好的類組織(不美好地方是,效率差了點,以及對OO思想非得有點水準(zhǔn),否則會繞暈掉,為了不讓Cer笑話,我們不提太多)。

七、不過,縱算如此(第六點),我還是是狡辯一句:就算是在那些看起來很純的OO語言里,其實也有友元的影子啊。比如Java,是沒有friend關(guān)鍵字,可以它的內(nèi)部類(非靜態(tài)的內(nèi)部類),可以直接訪問外部類,難道不是友元嗎?——事實上,這也正是在C++使用friend最主旋律的用法(我甚至不用寫之一)。再如Object Pascal(Delphi),是沒有friend關(guān)鍵字,可是只要是位于同一個代碼單元(就是同一個.pas文件),則其中所有類天生就是可以互相訪問啊(當(dāng)然,需首先滿足可見性)——這是當(dāng)年我用Delphi時覺得最爽的地方之一了,既然號稱更OO的語言都留了一手,為何C++不能呢? :)

八、第七點明顯帶有情緒化,這不符合C++之父對我們的期望:C++設(shè)計中有一條指導(dǎo)原則,那就是,無論做什么事情,都必須相信程序員。與可能出現(xiàn)什么樣的錯誤相比,更重要得多的是能做出什么好事情。C++程序員總被看作是成年人……。在C++的大千世界,差異性永遠(yuǎn)被尊重,有人不愛用template,那不用就是; 有人堅決認(rèn)為只要有privatepublic就足夠了,那就把protected忘記吧,甚至有人認(rèn)為virtual也是多余的——很多人就是把C++當(dāng)成另一種C使用,那都可以接受。friend也一樣,如果你不用,它的存在并不會給你帶來什么性能損失,你要做的就是用很OO或很不OO的,但是你熟悉的方法去滿足友元的需求而已。

九、一定要這個第9點。除了友元類,更常見的其實是友元函數(shù)。很多操作符的重載,都需要全局的友元函數(shù)來減輕相關(guān)類的public出太多成員。這一下扯到操作符重載有用嗎?哇,這是另外一個經(jīng)典的問題了,它曾經(jīng)引起的糾紛,比這個友元所帶來的,要熱鬧上幾倍呢。就此打住。

十、最后一點,C++初學(xué)者如何學(xué)習(xí)這門語言眾多的,又容易產(chǎn)生正交效應(yīng)的特性呢?我有個建議:先有個基本了解,做點練習(xí),但并不需要急著真正使用。

 

posted on 2009-12-02 10:29 肥仔 閱讀(949) 評論(2)  編輯 收藏 引用 所屬分類: C++ 基礎(chǔ)

評論

# re: 當(dāng)&ldquo;友元&rdquo;遇到&ldquo;虛函數(shù)&rdquo;  回復(fù)  更多評論   

好文~頂一個
2009-12-02 13:26 | iceshark

# re: 當(dāng)&ldquo;友元&rdquo;遇到&ldquo;虛函數(shù)&rdquo;  回復(fù)  更多評論   

好文,學(xué)習(xí)了
2009-12-02 16:01 | zhaoyg
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲精品国产精品国自产观看浪潮 | 极品av少妇一区二区| 欧美日本视频在线| 欧美韩日高清| 欧美日韩精品欧美日韩精品一 | 亚洲欧美bt| 久久国产精品电影| 理论片一区二区在线| 欧美黄色aaaa| 久久乐国产精品| 美国十次成人| 亚洲韩国一区二区三区| 99视频有精品| 欧美一区二区黄色| 老司机精品视频一区二区三区| 欧美国产日韩a欧美在线观看| 欧美日韩亚洲一区在线观看| 国产精品久久久久国产a级| 国产亚洲精品福利| 亚洲精品在线电影| 欧美专区第一页| 亚洲国产婷婷香蕉久久久久久| 洋洋av久久久久久久一区| 欧美永久精品| 欧美日韩免费一区二区三区视频 | 久久99伊人| 欧美精品在线免费| 国产亚洲精品久久久久动| 亚洲精品视频在线观看网站| 久久精品国产久精国产爱| 日韩亚洲一区二区| 久久蜜臀精品av| 国产精品亚洲一区| 亚洲人成在线免费观看| 久久成人一区| 一本色道久久88综合日韩精品| 久久精品国产久精国产一老狼| 欧美新色视频| 亚洲激情视频在线播放| 欧美一区二区三区精品电影| 欧美高清hd18日本| 欧美一级欧美一级在线播放| 欧美日本不卡| 91久久极品少妇xxxxⅹ软件| 欧美一区二区三区成人| 99国产精品99久久久久久粉嫩| 久久亚裔精品欧美| 国产日韩亚洲欧美| 亚洲免费视频在线观看| 最新成人在线| 免费视频亚洲| 亚洲国产成人porn| 久久永久免费| 欧美一区二区啪啪| 国产亚洲激情| 久久精品国产99国产精品澳门| 亚洲专区一区二区三区| 国产精品久久久久7777婷婷| 99精品国产一区二区青青牛奶| 欧美成人免费va影院高清| 亚洲欧美在线磁力| 国产精品一区二区视频| 性欧美8khd高清极品| 亚洲一二三级电影| 夜夜夜久久久| 开元免费观看欧美电视剧网站| 欧美一级艳片视频免费观看| 国产精品观看| 久久电影一区| 欧美一区二区三区视频在线观看| 蜜桃久久av| 久久久久久国产精品一区| 国产一区二区三区四区三区四| 香港久久久电影| 欧美一区二区在线免费播放| 激情国产一区二区| 亚洲第一在线综合网站| 欧美成人一区在线| 亚洲视频高清| 亚洲欧美日韩综合一区| 国产日韩在线看片| 久久亚洲二区| 欧美国产在线观看| 亚洲午夜伦理| 欧美一区二区免费视频| 在线精品一区| 亚洲精品久久久久久一区二区 | 亚洲黄网站在线观看| 欧美精品三级| 久久精品成人| 久久夜色精品国产| 在线视频一区二区| 欧美亚洲三级| 亚洲精品黄网在线观看| 一区二区不卡在线视频 午夜欧美不卡'| 欧美性理论片在线观看片免费| 久久嫩草精品久久久精品一| 免费亚洲网站| 亚洲永久在线观看| 久久国产精品毛片| 一本大道久久a久久精品综合 | 噜噜爱69成人精品| 一区二区三区四区五区视频 | 国产精品国产三级欧美二区| 久久中文欧美| 欧美日韩免费一区| 欧美国产一区二区在线观看| 国产精品多人| 欧美国产第二页| 国产欧美日本| 一区二区日韩欧美| 亚洲国产欧美精品| 欧美一级黄色录像| 亚洲欧美激情视频| 欧美人与性动交α欧美精品济南到| 欧美在线免费观看视频| 欧美日韩国产三级| 欧美成人午夜激情视频| 国产一区二区三区最好精华液| 一区二区动漫| 美女成人午夜| 国产精品黄色| 亚洲精品乱码| 亚洲国产精品久久| 久久精品噜噜噜成人av农村| 午夜精品久久久久久久99水蜜桃 | 欧美国产亚洲精品久久久8v| 国产区精品在线观看| 一本一本a久久| 一本色道久久综合狠狠躁篇怎么玩 | 亚洲福利视频在线| 在线观看亚洲精品| 欧美一区三区三区高中清蜜桃| 亚洲视频在线一区观看| 欧美高清视频在线观看| 亚洲成色www8888| 精品动漫3d一区二区三区免费版| 亚洲欧美综合国产精品一区| 亚久久调教视频| 国产精品专区一| 亚洲欧美日本另类| 久久精品五月| 伊人久久综合97精品| 久久国产精品亚洲va麻豆| 久久网站免费| 亚洲二区精品| 欧美freesex8一10精品| 亚洲国产精品va| 日韩性生活视频| 欧美日韩一区二区国产| 亚洲一区二区久久| 久久成人18免费网站| 在线精品国产欧美| 欧美激情二区三区| 亚洲视频在线观看视频| 久久国产视频网站| 亚洲电影免费在线观看| 欧美国产综合视频| 男人的天堂成人在线| 9i看片成人免费高清| 欧美午夜视频一区二区| 亚洲一级黄色片| 蜜桃av噜噜一区| 制服丝袜激情欧洲亚洲| 国产精品亚洲产品| 久久综合精品国产一区二区三区| 亚洲三级免费| 久久精品国产99国产精品| 亚洲成色777777在线观看影院| 欧美成人综合在线| 亚洲欧美999| 亚洲激情综合| 欧美主播一区二区三区| 日韩视频国产视频| 国产亚洲精品自拍| 欧美人成免费网站| 久久精品久久综合| 亚洲网址在线| 欧美国产第一页| 性欧美激情精品| 欧美sm视频| 亚洲免费激情| 国产人成精品一区二区三| 久久亚洲精选| 亚洲在线观看| 亚洲精品免费在线| 久久久亚洲国产天美传媒修理工| 亚洲精品在线电影| 精品动漫av| 国产亚洲精品v| 欧美日韩在线观看一区二区| 久久免费精品日本久久中文字幕| 9人人澡人人爽人人精品| 开元免费观看欧美电视剧网站| 亚洲欧美另类久久久精品2019| 亚洲欧洲精品一区二区三区不卡 | 欧美久久影院| 久久久亚洲国产美女国产盗摄| 亚洲小说欧美另类婷婷| 亚洲精品免费一二三区|