最近在写地瓜皮,使用命名空间同时使用友元函数的时候发生了一个神奇的compile error,经过思考,终于将问题解决了。现在发布出来,希望能够对大家有所帮助。
先简单说说命名空间。在写C工程的时候,尤其是万行以上的程序,命名空间冲突是一个很让人崩溃的事情。目前使用的最多的解决办法就是把函数的名字搞的非常非常长(学过GTK的同学应该有所体会)。在C++中,增加了一个叫做“命名空间”的特性,它由关键字“namespace”来声明、定义和使用。相信学过C++的同学对“命名空间”的使用方法都有了比较深入的理解,我在此就不再介绍了。
而当我们有时需要重载类的某些运算符的时候,又难以避免地用到“友元函数”。曾经有过大牛批判过“友元函数”破坏类的封装性。不过对于我这种低水平的coder,“友元函数”确实带来了不少方便的地方。
问题一:
先给大家看一个简单的代码:
- class istream;
- istream& operator>>(istream&,int&);
- class CA
- {
- int a;
- friend istream& operator>>(istream&,CA& );
- };
- istream& operator>>(istream& is,CA& a)
- {
- is>>a.a;
- return is;
- }
- int main()
- {
- extern istream stream;
- elephant::CA a;
- stream>>a;
- }
保存为a.cpp之后编译(不要链接)。
g++ -c a.cpp |
由于这里没有istream这个类的定义,所以链接肯定会失败。使用-c选项只编译不链接。
这时编译没有任何问题。
当我想把CA这个类包含在一个elephant命令空间后,出现问题了。用代码说话:
- class istream;
- istream& operator>>(istream&,int&);
- namespace elephant
- {
- class CA
- {
- int a;
- friend istream& operator>>(istream&,CA& );
- };
- }
- istream& operator>>(istream& is,elephant::CA& a)
- {
- is>>a.a;
- return is;
- }
- int main()
- {
- extern istream stream;
- elephant::CA a;
- stream>>a;
- }
同样,只编译,不链接,报告错误:
# 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行改成:
- 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一下。完整代码如下:
- class istream;
- istream& operator>>(istream&,int&);
- namespace elephant
- {
- class CA
- {
- int a;
- friend istream& operator>>(istream&,CA& );
- };
- istream& operator>>(istream&,CA&);
- }
- istream& elephant::operator>>(istream& is,elephant::CA& a)
- {
- is>>a.a;
- return is;
- }
- int main()
- {
- extern istream stream;
- elephant::CA a;
- stream>>a;
- }
只编译,不链接。编译成功。
总结:
1、friend没有对函数进行声明,所以我们要另外声明一下这个函数。friend仅仅是告诉这个类,这个函数对这个类的私有成员有访问权限。
2、在命名空间内部进行声明的函数已经被包含进了命名空间,在定义的时候要使用命名空间说明符。
3、friend仅仅说明了elephant::operator>>函数是类CA的友元函数,而operator>>函数并不是类CA的友元函数。所以operator>>访问类CA的私有成员,编译器会报告错误。
4、elephant::operator>>与operator>>并不是一个函数。前者是定义在elephant命名空间下的函数,而后者则是定义在全局的函数。
问题二:
还是一个简单的代码:
- void output(int a);
- class SA
- {
- int data;
- friend void output(const SA&);
- };
- void output(const SA& a)
- {
- output(a.data);
- }
- int main()
- {
- SA a;
- output(a);
- }
只编译,不链接。没有任何问题。现在,同样的,我要将类SA放在elephant命名空间里。根据问题一的讨论,我们也得把output函数放在elephant命名空间里。代码如下:
- namespace elephant
- {
- class SA
- {
- int data;
- friend void output(const SA&);
- };
- void output(int a);
- void output(const SA&);
- }
- void elephant::output(const elephant::SA& a)
- {
- output(a.data);
- }
- int main()
- {
- elephant::SA a;
- elephant::output(a);
- }
只编译,不链接。没有任何问题。
可是,在我的地瓜皮里面,由于某种特殊的需要,我希望output函数及它所有的重载函数全都定义在全局空间里。怎么办?
我进行了两次尝试。第一个尝试是失败的,上代码:
- void output(int a);
- namespace elephant
- {
- class SA;
- }
- void output(const elephant::SA&);
- namespace elephant
- {
- class SA
- {
- int data;
- friend void output(const SA&);
- };
- }
- void output(const elephant::SA& a)
- {
- output(a.data);
- }
- int main()
- {
- elephant::SA a;
- output(a);
- }
由于output函数需要用到elephant::SA类型的参数,所以对elephant::SA做了一个前向声明。但是,明显的,这是不行的。根据问题一的讨论,elephant::output对elephant::SA的私有成员有访问权限,而output没有。
如何解决这个问题?或者说,如何将全局空间的output函数声明为elephant::SA的友元函数。答:使用全局空间说明符。上代码:
- void output(int a);
- namespace elephant
- {
- class SA;
- }
- void output(const elephant::SA&);
- namespace elephant
- {
- class SA
- {
- int data;
- friend void ::output(const SA&);
- };
- }
- void output(const elephant::SA& a)
- {
- output(a.data);
- }
- int main()
- {
- elephant::SA a;
- output(a);
- }
只编译,不链接。成功。
“::”前面是一个空格,这个叫做全局空间说明符。相信大家在学习C++的时候已经学过。在此不再详细解释。
总结:
1、在命名空间内定义类的友元函数的时候,它默认是指向相同命名空间内的函数。
2、如需指向全局空间内的函数,需要使用全局空间说明符说明。
相关信息:
操作系统:ubuntu linux 10.10 maverick
编译器:g++ version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5)
转载于:https://blog.51cto.com/elephantliu/610844