C++命名空间与友元函数

来源:互联网 发布:淘宝发优惠劵工作 编辑:程序博客网 时间:2024/06/06 02:47

转自:http://elephantliu.blog.51cto.com/1107116/610844

C++命名空间与友元函数 2011-07-14 12:47:57

标签:C++namespace命名空间 友元函数
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://elephantliu.blog.51cto.com/1107116/610844

     最近在写地瓜皮,使用命名空间同时使用友元函数的时候发生了一个神奇的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


原创粉丝点击