friend ostream &operator<<(const object &o) //作为ostream的类函数
如Java,Scala,Python都不支持独立与类存在定义的函数了。在面向对象的角度,后续的语言实现的更加纯粹了。所以如果本身代码风格趋近与面向对象的风格,就应该尽量理由友元类来实现需要的功能,而不是使用友元函数。有朋友私信我说:Python之中明明直接def就可以定义函数了,也不需要类啊,这是不是也不符合面向对象的逻辑思维?这里简单解释一下,Python之中的每个函数,都会被包装成一个function对象,所以Python之中是一切皆对象的,不会存在独立与类存在的函数。而如同Java与Scala之中的lambda表达式,也是包装为匿名类存在的。
2)友元类
通过友元类包装之中,友元类摇身一变,类之中所有的方法都成为友元函数了。看起来并不会破坏上文提到的面向对象的逻辑了,但是涉及到继承又存在一些不大不小的坑,我们一起来捋一捋:
友元关系不能继承。基类的友元对派生类的成员没有特殊访问权限。如果基类被授予友元关系,则只有基类具有特殊访问权限,该基类的派生类不能访问授予友元关系的类。
好复杂,我们直接上代码:
#include <iostream> using namespace std; class A { private: int x; friend class C; }; class B:public A{ private: int y; }; class C { voidprintA(A& a) { cout << a.x << endl; } voidprintB(B& b) { cout << b.x << endl; //C类依然可以利用友元关系访问从B类从A类继承来的私有变量 //cout << b.y << endl; C类不可以访问B类的私有变量,友元关系不继承,该语句不合法。 } };
显然,类C与A的友元关系止步于继承处,类C没法访问类B新定义的私有变量。(这里留一个小问题给大家,如果类B覆盖了类A的私有变量x,类C之中的printB是否仍然可以通过编译呢?)
我们再看一段不同的代码:
#include <iostream> using namespace std; class A { int x; public: friend class B; }; class B { public: voidfun(A& a){ cout << a.x << endl;} }; class C:public B { public: //void fun2(A& a){ cout <<a.a <<endl;} //派生类新加的函数却不能访问A,此句会报错 }; voidmain() { A a; C c; c.fun(a); //C是B的派生类,只能基类B的函数fun来访问A的对象 }
类C虽然继承了类B,但是也不在具有了和A的友元关系,只能"拼爹"。依赖从类B之中继承的友元函数来访问类A。(这里同样留一个小问题给大家,如果类B之中的fun函数是protected或private的,那上述代码还能够正常编译吗?)
在这里做一个简单的小结:友元关系在友元类之中没有继承性,只能依赖基类的友元关系。
3.非C++语言是怎么解决友元关系的:Java
JAVA修饰符类型(public,protected,private)
public的类、类属变量及方法,包内及包外的任何类均可以访问;
protected的类、类属变量及方法,包内的任何类,及包外的那些继承了此类的子类才能访问;
private的类、类属变量及方法,包内包外的任何类均不能访问;
如果一个类、类属变量及方法不以这三种修饰符来修饰。那么包内的任何类都可以访问它,而包外的任何类都不能访问它(包括包外继承了此类的子类)。所以这种类型有时也称为friendly类型,现在知道这个名字的出处了吧,大家对同一个package之中要放哪些类有木有新的认识了呢?