一、概念
定义:
类实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,仅能通过类的成员函数才能读写。如果数据成员定义为公共的,则又破坏了封装性。但是某些情况下,需要频繁读写类的成员,特别是在对某些成员函数多次调用时,由于参数传递、类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。友元是一种定义在类外部的普通函数,但他需要在类体内进行说明,为了和该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,但是他能够访问类中的所有成员。
作用:
在于提高程序的运行效率,但是,他破坏了类的封装性和隐藏性,使得非成员函数能够访问类的私有成员。导致程序维护性变差,因此使用友元要慎用。友元较为实际的应用是在运算符重载,这种应用可以提高软件系统的灵活性。
分类:
- 友元函数
- 友元类
- 友元成员函数
二、友元函数
#include <iostream>
using namespace std;
class Girl
{
private:
int age;
public:
Girl(int age):age(age){}
int get_age() const
{
cout << &age << endl;
return 18;
}
// 1. "声明"友元函数
friend void access_true_age(Girl&);
};
// 2. 定义友元函数
void access_true_age(Girl& g)
{
// 突破权限
cout << &g.age << endl;
cout << "真实年龄:" << g.age << endl;
// 修改
g.age = 18;
cout << "修改后年龄:" << g.age << endl;
}
int main()
{
Girl g(45);
cout << g.get_age() << endl;
// 通过友元函数访问Girl的年龄
access_true_age(g);
return 0;
}
- 由于不属于类的成员函数,因此友元函数没有this指针,访问类的成员只能通过对象。
- 友元函数在类中的“声明”可以写在类的任何部分,不受权限修饰符的影响。
- 理论上一个友元函数可以是多个类的友元函数,只需要在各个类中分别“声明”。
三、友元类
当一个类B成为了另一个类A的友元类时,类B可以访问类A的所有成员。
需要注意的是:
- 友元关系是单向的,不具有交换性。
如果类B是类A的友元类,类A不一定是类B的友元类。
- 友元关系不具有传递性。
如果类C是类B的友元类,类B是类A的友元类,类C不一定是类A的友元类。
- 友元关系不能被继承。
#include <iostream>
using namespace std;
class A
{
private:
string str = "A私有";
// “声明”友元类
friend class B;
};
class B
{
public:
void func(A& a)
{
// cout << this->str << endl; 错误:this是B对象不是A对象
cout << a.str << endl;
a.str = "我改了";
cout << a.str << endl;
}
};
int main()
{
A a;
// cout << a.str << endl; 错误
B b;
b.func(a);
return 0;
}
四、友元成员函数
在友元类的任何成员函数中都可以访问其他类的成员,但是友元成员函数把友元范围限制在一个成员函数中。
例如,类B的某个成员函数称为了类A的友元成员函数,这样类B的该成员函数就可以访问类A的所有成员了。
#include <iostream>
using namespace std;
// 3. 因为第二步中用到了类A,提前声明类A
class A;
// 2. 编写类B,并真正声明友元成员函数
class B
{
public:
void func(A&);
};
class A
{
private:
string str = "A私有";
// 1. 确定友元的函数格式并“声明”
friend void B::func(A&);
};
// 4. 类外定义友元成员函数
void B::func(A & a)
{
// cout << this->str << endl; 错误:this是B对象不是A对象
cout << a.str << endl;
a.str = "我改了";
cout << a.str << endl;
}
int main()
{
A a;
// cout << a.str << endl; 错误
B b;
b.func(a);
return 0;
}