天天看點

《深入了解C++11:C++ 11新特性解析與應用》——2.9 擴充的friend文法

類别:部分人

friend關鍵字在c++中是一個比較特别的存在。因為我們常常會發現,一些面向對象程式語言,比如java,就沒有定義friend關鍵字。friend關鍵字用于聲明類的友元,友元可以無視類中成員的屬性。無論成員是public、protected或是private的,友元類或友元函數都可以通路,這就完全破壞了面向對象程式設計中封裝性的概念。是以,使用friend關鍵字充滿了争議性。在通常情況下,面向對象程式開發的專家會建議程式員使用get/set接口來通路類的成員,但有的時候,friend關鍵字确實會讓程式員少寫很多代碼。是以即使存在争論,friend還是在很多程式中被使用到。而c++11對friend關鍵字進行了一些改進,以保證其更加好用。我們可以看看下面的例子,如代碼清單2-19所示。

《深入了解C++11:C++ 11新特性解析與應用》——2.9 擴充的friend文法
《深入了解C++11:C++ 11新特性解析與應用》——2.9 擴充的friend文法

在代碼清單2-19中,我們聲明了3個類型:lilei、jim和hanmeimei,它們都有一個友元類型poly。從編譯通過與否的狀況中我們可以看出,在c++11中,聲明一個類為另外一個類的友元時,不再需要使用class關鍵字。本例中的jim和hanmeimei就是這樣一種情況,在hanmeimei的聲明中,我們甚至還使用了poly的别名p,這同樣是可行的。

雖然在c++11中這是一個小的改進,卻會帶來一點應用的變化—程式員可以為類模闆聲明友元了。這在c++98中是無法做到的。比如下面這個例子,如代碼清單2-20所示。

《深入了解C++11:C++ 11新特性解析與應用》——2.9 擴充的friend文法

從代碼清單2-20中我們看到,對于people這個模闆類,在使用類p為模闆參數時,p是people

的一個friend類。而在使用内置類型int作為模闆參數的時候,people會被執行個體化為一個普通的沒有友元定義的類型。這樣一來,我們就可以在模闆執行個體化時才确定一個模闆類是否有友元,以及誰是這個模闆類的友元。這是一個非常有趣的小特性,在編寫一些測試用例的時候,使用該特性是很有好處的。我們看看下面的例子,該例子源自一個實際的測試用例,如代碼清單2-21所示。

《深入了解C++11:C++ 11新特性解析與應用》——2.9 擴充的friend文法
《深入了解C++11:C++ 11新特性解析與應用》——2.9 擴充的friend文法
《深入了解C++11:C++ 11新特性解析與應用》——2.9 擴充的friend文法

在代碼清單2-21所示的這個例子中,測試人員的目的是在一系列函數調用後,檢查attacker類變量a和defender類變量d中成員變量的值是否符合預期。這裡,按照封裝的思想,所有成員變量被聲明為private的。但attacker和defender的開發者為了友善,并沒有為每個成員寫get函數,也沒有為attacker和defender增加友元定義。而測試人員為了能夠快速寫出測試程式,采用了比較危險的做法,即使用宏将private關鍵字統一替換為public關鍵字。這樣一來,類中的private成員就都成了public的。這樣的做法存在4個缺點:一是如果僥幸程式中沒有變量包含private字元串,該方法可以正常工作,但反之,則有可能導緻嚴重的編譯時錯誤;二是這種做法會降低代碼可讀性,因為改變了一個常見關鍵字的意義,沒有注意到這個宏的程式員可能會非常迷惑程式的行為;三是如果在類的成員定義時不指定關鍵字(如public、private、protect等),而使用預設的private成員通路限制,那麼該方法也不能達到目的;四則很簡單,這樣的做法看起來也并不漂亮。

不過由于有了擴充的friend聲明,在c++11中,我們可以将defender和attacker類改良一下。我們看看下面的例子,如代碼清單2-22所示。

《深入了解C++11:C++ 11新特性解析與應用》——2.9 擴充的friend文法
《深入了解C++11:C++ 11新特性解析與應用》——2.9 擴充的friend文法
《深入了解C++11:C++ 11新特性解析與應用》——2.9 擴充的friend文法

在代碼清單2-22中,我們把原有的defender和attacker類定義為模闆類defendert和attackert。而在需要進行測試的時候,我們使用validator為模闆參數,執行個體化出defendertest及attackertest版本的類,這個版本的特點是,validator是它們的友元,可以任意通路任何成員函數。而另外一個版本則是使用int類型進行執行個體化的defender和attacker,按照c++11的定義,它們不會有友元。是以這個版本保持了良好的封裝性,可以用于提供接口用于正常使用。

值得注意的是,在代碼清單2-22中,我們使用了using來定義類型的别名,這跟使用typedef的定義類型的别名是完全一樣的。使用using定義類型别名是c++11中的一個新特性,我們可以在3.10節中看到相關的描述。

繼續閱讀