C++中的friend究竟有什麼用?
如果設計得當,應該可以完全避免使用friend,friend還會暴露所有私有以及保護成員……正式項目中,friend究竟有多少應用,應用在什麼地方?
Containers iterator is friend of the container.
分享一個今天的例子吧,這是我第一次被友元觸到了g點。
今天我在寫一個神經網路,寫完以後想寫一個單元測試,和神經網路放在一個命名空間里。我寫的神經網路是一個類,裡面有多個私有變數,而單元測試不可避免地要用到這些私有變數。如果這是在java里,我就只能再寫上一堆訪問器了;而在C++里,我只用把這個單元測試函數聲明成類的友元,就能隨心所欲地訪問私有變數,簡直不要太痛快。現在感覺,C++提供了很多你90%的時間都不會使用的特性,但是在10%的場景里,這些特性會讓你無比酸爽。java 有內部類。(inner class. c++ 通常叫嵌套類)
java 的內部類分兩種。
一種叫靜態內部類,一種是普通內部類。
java 的靜態內部類 和 c++的嵌套類基本對應。
但,JAVA的普通內部類,可就神奇多了。
要new它(假設new 出來稱為對象i),你得先擁有一個外部類的對象(假設為對象e,如果創建內部煩的代碼就是外部類的的非靜態方法,那麼就是this)。然後,i 是可以默認訪問e的一切成員。c++中使用友元(friend)最主要的場合,就是在嵌套類和外部類之間。
JAVA砍掉Friend,是因為它把C++中最經常使用FRIEND的案例,直接固定成一種語法了。
現在問題來了: 一,外部類可以訪問內部類的私有成員嗎? 二,內部類和外部類如果有成員同名,而內部類又想訪問外部內那個成員,怎麼辦?三,外部類的靜態方法和非靜態方法訪問內部類有什麼不同?四在類方法中定義的類,稱為局部內部類,它和外部類又是什麼關係?最後,挖掘機技術哪家強?
我個人是不喜歡JAVA在這裡的設計的,還不如C++。直接給你一個friend加一條規則:只要有一個類願意聲明你是朋友,那你就可以摸她的一切。哪要JAVA的靜態內部類,非靜態內部類,隱藏的外部對象(對應隱藏的THIS對象),局部內部類,靜態方法訪問時。。。非靜態方法訪問時。。。內部類天生擁有外部類成員。。。。煩呢。
你要說,c++那條關於friend的規則沒有說明友元關係是單向的還是雙向的?那你自個想想,你聲明章子怡是你的朋友,允許她摸你的一切,然後,你因此也摸子怡的一切,那人家峰哥能同意嗎?所以在C++中,友元關係是單向的。
一般重載操作符的時候用的比較多。尤其是重載的模板類操作符需要支持該模板類對象隱式轉換的時候。
就是耦合(貶義詞)或者內聚(褒義詞)唄。 有的事情其實應該一個類實現,但是語法讓這個事情在一個類里實現有困難,那就讓他們分2個類實現並且聲明為友元吧。。
friend(朋友)是一支矛♂,刺破面向對象♀信息隱藏的盾♀。
可以避免吧,Java和C#都沒有,大家也用得挺好的。
我用friend(和我看到的用friend)的情況基本都是:我的一個庫,有好多class,class A有一些成員或方法是私有的,也就是不願意讓庫之外的用戶看到並使用的東西。但是我在這個庫的class B裡面想要用,這個時候就得要friend了。
C#裡面有個internal的修飾符,貌似可以干類似的事情親,friend主要用來破class的物理防禦的。設想重載
能不用最好,但有時也很難避免,特別是主類+輔助類這種設計方式,輔助類需要可以訪問主類的私有成員。在這裡輔助類實際邏輯上是主類的一部分,但是物理上分開更好,常見的輔助類比如迭代器,守護器等等...
友元——訪問規則蛋皮上的裂縫
然而經常會有一些特殊的訪問需求
你可以看一下java的default也就是包訪問,在c++里,就得用friend搞一下是否使用有友員是一個見仁見智的問題,大家說法各不相同。
根據美國留學時期教授對friend class的評論,以及工作中對企業代碼規則的理解,我認為,正式項目中,應該避免使用友員。
友員對coder大腦造成額外負擔(智力的以及情緒的),性價比很低。避開友員,則class的access model變得簡單。對於正式項目,便於溝通、易於理解是首要的準則。維護和改進代碼的是"人們",不是"一個人",更不是"機器"。寫迭代器、單測等等
friend最大的作用就是用於運算符重載,這種場景下,訪問私有成員變得更方便,而且運算符重載通常無需考慮類之間的關聯和獨立性,它本身即是一個特殊的訪問器(accessor)
其他場景下
friend是侵入性的,破壞了封裝性,使得OO機制的核心——繼承和virtual成為浮雲強行使用friend對兩個類進行關聯,導致二者不再相互獨立,在內部邏輯關聯緊密的情況下,很多修改會變成牽一髮而動全身的態勢,後患無窮
如果僅僅是覺得friend使訪問類的私有成員變得方便,我建議你放棄這個想法,老老實實地使用訪問器(accessor)來操作重載操作符的時候,有用啊,訪問私有成員和成員函數訪問私有成員有多大區別呢,都是一個介面
序列化的時候用得著
class car{
friend mockCar}Unit test{ MockCar }你可以把C++理解成一個「大雜燴」。
取支持的所有語法特性中的一個比較安全的「小子集」用。所有不安全的語法特性,都閹割掉,你不用,你的代碼就相對安全。等你有3~5年寫C++的經驗再用。
按c with class的方式寫C++未嘗不可。c的坑很多,C++更多,JAVA等語言不支持的語法特性,C++里有的,都可以不用。比如多繼承,都有替代的實現方案。你放心,人JAVA敢閹割掉,就說明有替代方案,勿多慮。
本質上解決一種問題情景,都有多種思路方式可以實現。JAVA,c#等後出現語言,都做了取捨優化。
等你了解的語言夠多,就會發現設計思想都是差不多,無非不同語言有不同的實現或者叫法。誰也並不比誰在理念上高明太多。
就好比中文叫蘋果,英文叫apple,指的都是一回事。
面向對象,過程式只是解題思路或者說對題目的理解不同。
解決問題是關鍵,不要在意這些細節。另外,可以了解一下Python之類的語言。人生苦短,少用C++。推薦閱讀:
※如何寫優美的c++代碼?
※為什麼說:最優秀的程序員大部分是C程序員?
※如何用C++實現一個視頻聊天伺服器,要用到那些協議和庫?
※c++哪個json序列化庫好?
※為什麼使用virtual關鍵字在C++與C#會出現不同的效果?求解答。