程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C++命名空間與友元函數

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

編輯:關於C語言

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

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

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

    問題一:

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

  1. class istream; 
  2. istream& operator>>(istream&,int&); 
  3. class CA 
  4.     int a;
  5.     friend istream& operator>>(istream&,CA& ); 
  6. }; 
  7. istream& operator>>(istream& is,CA& a) 
  8.     is>>a.a; 
  9.     return is; 
  10. }
  11. int main() 
  12.    extern istream stream; 
  13.    elephant::CA a; 
  14.    stream>>a;
  15. }

     保存為a.cpp之後編譯不要鏈接)。

  g++ -c a.cpp

     由於這裡沒有istream這個類的定義,所以鏈接肯定會失敗。使用-c選項只編譯不鏈接。

    這時編譯沒有任何問題。

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

 

  1. class istream; 
  2. istream& operator>>(istream&,int&); 
  3. namespace elephant 
  4.     class CA 
  5.     { 
  6.         int a;
  7.         friend istream& operator>>(istream&,CA& ); 
  8.     }; 
  9. istream& operator>>(istream& is,elephant::CA& a) 
  10.     is>>a.a; 
  11.     return is; 
  12. int main() 
  13.    extern istream stream; 
  14.    elephant::CA a; 
  15.    stream>>a;
  16. }

    同樣,只編譯,不鏈接,報告錯誤:

#  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行改成:

 

  1. istream& elephant::operator>>(istream& is,elephant::CA& a) 

    只編譯,不鏈接,報告錯誤:   

#  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. class istream; 
  2. istream& operator>>(istream&,int&); 
  3. namespace elephant 
  4.     class CA 
  5.     { 
  6.         int a;
  7.         friend istream& operator>>(istream&,CA& ); 
  8.     }; 
  9.     istream& operator>>(istream&,CA&); 
  10. istream& elephant::operator>>(istream& is,elephant::CA& a) 
  11.     is>>a.a; 
  12.     return is; 
  13. int main() 
  14.     extern istream stream; 
  15.     elephant::CA a; 
  16.     stream>>a; 

    只編譯,不鏈接。編譯成功。

    總結:

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

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

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

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

 

    問題二:

        還是一個簡單的代碼:

 

  1. void output(int a); 
  2. class SA 
  3.     int data; 
  4.     friend void output(const SA&); 
  5. }; 
  6. void output(const SA& a) 
  7.     output(a.data); 
  8. int main() 
  9.     SA a; 
  10.     output(a); 

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

 

  1. namespace elephant 
  2.     class SA 
  3.     { 
  4.         int data; 
  5.         friend void output(const SA&); 
  6.     }; 
  7.     void output(int a); 
  8.     void output(const SA&); 
  9. void elephant::output(const elephant::SA& a) 
  10.     output(a.data); 
  11. int main() 
  12.     elephant::SA a; 
  13.     elephant::output(a); 

    只編譯,不鏈接。沒有任何問題。

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

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

   

  1. void output(int a); 
  2. namespace elephant 
  3.     class SA; 
  4. void output(const elephant::SA&); 
  5. namespace elephant 
  6.     class SA 
  7.     { 
  8.         int data; 
  9.         friend void output(const SA&); 
  10.     }; 
  11. void output(const elephant::SA& a) 
  12.     output(a.data); 
  13. int main() 
  14.     elephant::SA a; 
  15.     output(a); 

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

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

   

  1. void output(int a); 
  2. namespace elephant 
  3.     class SA; 
  4. void output(const elephant::SA&); 
  5. namespace elephant 
  6.     class SA 
  7.     { 
  8.         int data; 
  9.         friend void ::output(const SA&); 
  10.     }; 
  11. void output(const elephant::SA& a) 
  12.     output(a.data); 
  13. int main() 
  14.     elephant::SA a; 
  15.     output(a); 

    只編譯,不鏈接。成功。

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

    總結:

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

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

 

 

    相關信息:

        操作系統:ubuntu linux 10.10 maverick

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

 

本文出自 “elephant_liu” 博客,請務必保留此出處http://elephantliu.blog.51cto.com/1107116/610844

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved