天天看点

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)

继续阅读