友元概念产生的背景
多态、封装、继承被称为C++语言的三大特性,但是友元的出现却在某种程度上破坏类的封装性,因此java抛弃了友元这个概念。当然友元也有着自己应用场景,当一个类或者函数想访问另一个类中的私有数据成员的时候,应该使用freind关键字,将函数或者类声明为友元来实现这一目的。从友元概念的取舍上,我们也可以看出C++和java之间的一个差异,C++ 语言虽然复杂,但是其有着更强的灵活性,这也就是为什么我们常说学好了C++那么java就不在话下的原因之一。
我们应该如何声明友元?
在讨论此问题之前,我们首先要明白声明友元的作用仅仅是提供给函数或者类特殊的访问权限,友元本身并非普通意义上的声明。如何理解呢?按照惯例,我们还是通过一个具体的例子来解释。
struct X{
friend void f(){} //此处并不是通常意义的函数声明
X(){ f(); } //错误,f还没有被声明
void g();
void h();
};
void X::g() {return f(); }//错误,f还没有被声明
void f(); //声明那个定义在X中的友元函数
void X::h() { return f(); }//正确,f的声明在作用域中
注意:事实上,很多编译器并没有强制限定友元函数必须在使用之前在类的外部进行声明,但是为了避免不必要的麻烦,屏蔽不同编译器之间的差异性,我们最好在使用友元之前对友元进行声明。
那些你容易忽略的小细节
- 如果一个类想把一组重载函数作为友元,要将每一个函数都声明为友元,这是因为重载的每一个函数都是独立的。
extern std::ostream& storeOn(std::ostream&,Screen&);
extern BitMap& storeOn(Bitmap &,Screen&);
class Screen{
//storeOn的ostream版本可以访问Screen的私有数据成员
friend std::ostream& storeOn(std::ostream&,Screen&);
};
本例中,Screen类把接受ostream&的storeOn函数声明成它的友元,但是接受的参数Bitmap的仍然不能访问Screen。
- 友元关系不存在传递性。举个例子,如果A是B友元,而B是C的友元,不能推出A是C的友元。倘若想要A是C的友元,应该单独进行声明。