一、友元的目的
先来扯一些为什么要用友元这么个东东。首先,类中不管是数据成员还是成员函数,都有各自不同的访问权限,有的是public,有的是private,还有的是protected。对于类的封装特性,一般都会把数据成员的访问权限设置为private,那么外部函数或者其他类想访问该类的数据成员怎么办?
第一种方法:
class A{
private:
int x;
int y;
int z;
public:
void getX(){
return x;
}
void getY(){
return y;
}
void getZ(){
return z;
}
};
void print(A& a){
cout << a.getX() << a.getY() << a.getZ << endl;
}
可以向上面代码一样,在A类里定义公共的接口,那么print调用三个接口就可以获得A的私有数据成员x, y, z。这么做并没有什么问题对吧,但是如果print()执行一万次、一千万次呢,就需要每次都调用三个接口,这样效率是非常慢的。那么就要用到友元了。
二、友元函数
先把上面的代码改成友元,看一下效果:
class A{
private:
int x;
int y;
int z;
public:
friend void print(A &a);
};
void print(A& a){
cout << a.x << a.y << a.z << endl;
}
是不是看起来简单多了,声明print函数为类A的友元,便可以随意的访问类A的私有成员。从这便可以看出友元的特点:
1)破坏了类的封装性;
2)提高了效率。
前面说的是友元函数是一个全局函数,用样也可以是类中的成员函数:
class A;
class B{
public:
void print(A &a)
};
class A{
private:
int x;
int y;
int z;
public:
friend void B::print(A &a);
};
void B::print(A& a){
cout << a.x << a.y << a.z << endl;
}
这个结构就有点复杂了,首先类B的成员函数print要被声明为类A的友元,不能这样写:
class A{
private:
int x;
int y;
int z;
public:
friend void B::print(A &a);
};
class B{
public:
void print(A& a){
cout << a.x << a.y << a.z << endl;
}
}
类A中有
friend void B::print(A &a);
这么一个玩意,那类B从哪来的呢,或许会说把类B放到前面进行声明,那这个地方就要注意一下,这里对前向声明做个小小的解释:
前向声明是一种不完全类型的声明,只提供类名,不提供实现。那么就有一定的局限性:
1.不能定义类的对象;
2.可以定义指向这个类型的指针或引用;
3.使用这个类型当做函数形参类型或者函数返回值类型。
声明针对指针或者引用,因为指针引用的大小系统是能够确定的,如果把类B放在前面声明,但是系统仍然不知道类B占用的空间是多少,应该开辟多少空间给他,而且前向声明没有实现,不知道B中有print函数,so类B放在前面声明仍然报错,那就说了,直接把类B全部内容放到类A的前面:
class B{
public:
void print(A& a){
cout << a.x << a.y << a.z << endl;
}
}
class A{
private:
int x;
int y;
int z;
public:
friend void B::print(A &a);
};
问题又来了,类B的成员函数print有这么个玩意:
void print(A& a)
那A又从哪来的,这里类A是个引用,故可以在类B前面声明类A,还有问题,print里面还有:
cout << a.x << a.y << a.z << endl;
这么个玩意,x, y, z从哪里的呢,在类A的前向声明并不告知x, y, z这些东西,并不知道类A中有什么,那么该怎么办呢?这时候就把print函数放到类B外面,类A的下面定义, 最终就是这样的:
class A;
class B{
public:
void print(A &a);
};
class A{
private:
int x;
int y;
int z;
public:
friend void B::print(A &a);
};
void B::print(A& a){
cout << a.x << a.y << a.z << endl;
}
三、友元类
上面说的都是友元函数,当一个类B专门用来操纵另一个类A时,如果类B中的所有成员函数都定义为类A的友元是非常麻烦的,这时候友元类就非常方便了,直接把类B定义为类A的友元类,同样举个例子:
class NUM{
private:
int x;
int y;
public:
NUM(int x, int y): x(x), y(y){}
friend class OPT;
};
class OPT{
public:
void add(NUM &n){
cout << n.x + n.y << endl;
}
void reduce(NUM &n){
cout << n.x - n.y << endl;
}
void multiply(NUM &n){
cout << n.x * n.y << endl;
}
void divide(NUM &n){
cout << n.x / n.y << endl;
}
}
友元类相比友元的类成员函数要简单很多,不用提前声明。
友元类终归一句话:
声明为谁的友元,就可以通过谁的对象,访问谁的私有成员。(好好体会这句话)
这里说一点,友元的声明位置不重要,可以在类的private下,也可以在public,也可以在protected,不影响,但一般建议放在public下,因为是拿来公用的嘛。
注意事项:
这里说一下友元的关系:
1.友元不能被继承,也就是说父类B是类A的友元类,那类B的子类C不一定是类A的友元类
2.友元不具有传递性,类B是类A的友元类,类A的是类C的友元类,但类B不一定是类C的友元类
2.友元不具有双向性,类B是类A的友元类,类A不一定是类B的友元类