天天看點

C++命名空間與友元函數

     最近在寫地瓜皮,使用命名空間同時使用友元函數的時候發生了一個神奇的compile error,經過思考,終于将問題解決了。現在釋出出來,希望能夠對大家有所幫助。

     先簡單說說命名空間。在寫C工程的時候,尤其是萬行以上的程式,命名空間沖突是一個很讓人崩潰的事情。目前使用的最多的解決辦法就是把函數的名字搞的非常非常長(學過GTK的同學應該有所體會)。在C++中,增加了一個叫做“命名空間”的特性,它由關鍵字“namespace”來聲明、定義和使用。相信學過C++的同學對“命名空間”的使用方法都有了比較深入的了解,我在此就不再介紹了。

     而當我們有時需要重載類的某些運算符的時候,又難以避免地用到“友元函數”。曾經有過大牛批判過“友元函數”破壞類的封裝性。不過對于我這種低水準的coder,“友元函數”确實帶來了不少友善的地方。

    問題一:

    先給大家看一個簡單的代碼:

     儲存為a.cpp之後編譯(不要連結)。

  g++ -c a.cpp

     由于這裡沒有istream這個類的定義,是以連結肯定會失敗。使用-c選項隻編譯不連結。

    這時編譯沒有任何問題。

    當我想把CA這個類包含在一個elephant指令空間後,出現問題了。用代碼說話:

    同樣,隻編譯,不連結,報告錯誤:

#  g++ -c a.cpp

a.cpp: In function ‘istream& operator>>(istream&, elephant::CA&)’:

a.cpp:7: error: ‘int elephant::CA::a’ is private

a.cpp:13: error: within this context

    說啥?說a是私有成員,不能通路。我都讓你friend了,你告訴我不能通路,這是怎麼回事?

    我們嘗試修改一下代碼,将operator>>函數也加入elephant命名空間,即,将第11行改成:

    隻編譯,不連結,報告錯誤:   

#  g++ -c a.cpp

a.cpp:11: error: ‘istream& elephant::operator>>(istream&, elephant::CA&)’ should have been declared inside ‘elephant’

    它說這個函數應該在elephant命名空間裡面declare一下(相信大家能夠區分declare和define的差別,前一個是“聲明”,後一個是“定義”)。那麼我們就declare一下。完整代碼如下:

    隻編譯,不連結。編譯成功。

    總結:

        1、friend沒有對函數進行聲明,是以我們要另外聲明一下這個函數。friend僅僅是告訴這個類,這個函數對這個類的私有成員有通路權限。

        2、在命名空間内部進行聲明的函數已經被包含進了命名空間,在定義的時候要使用命名空間說明符。

        3、friend僅僅說明了elephant::operator>>函數是類CA的友元函數,而operator>>函數并不是類CA的友元函數。是以operator>>通路類CA的私有成員,編譯器會報告錯誤。

        4、elephant::operator>>與operator>>并不是一個函數。前者是定義在elephant命名空間下的函數,而後者則是定義在全局的函數。

    問題二:

        還是一個簡單的代碼:

    隻編譯,不連結。沒有任何問題。現在,同樣的,我要将類SA放在elephant命名空間裡。根據問題一的讨論,我們也得把output函數放在elephant命名空間裡。代碼如下:

    隻編譯,不連結。沒有任何問題。

    可是,在我的地瓜皮裡面,由于某種特殊的需要,我希望output函數及它所有的重載函數全都定義在全局空間裡。怎麼辦?

    我進行了兩次嘗試。第一個嘗試是失敗的,上代碼:

    由于output函數需要用到elephant::SA類型的參數,是以對elephant::SA做了一個前向聲明。但是,明顯的,這是不行的。根據問題一的讨論,elephant::output對elephant::SA的私有成員有通路權限,而output沒有。

    如何解決這個問題?或者說,如何将全局空間的output函數聲明為elephant::SA的友元函數。答:使用全局空間說明符。上代碼:

    隻編譯,不連結。成功。

    “::”前面是一個空格,這個叫做全局空間說明符。相信大家在學習C++的時候已經學過。在此不再詳細解釋。

        1、在命名空間内定義類的友元函數的時候,它預設是指向相同命名空間内的函數。

        2、如需指向全局空間内的函數,需要使用全局空間說明符說明。

    相關資訊:

        作業系統:ubuntu linux 10.10 maverick

        編譯器:g++ version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)

繼續閱讀