我们知道类的私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。这固然能够带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书写的麻烦。
C++
设计者认为, 如果有的程序员真的非常怕麻烦,就是想在类的成员函数外部直接访问对象的私有成员,那还是做一点妥协以满足他们的愿望为好,这也算是眼前利益和长远利益的折中。因此,C++
就有了友元(friend
)的概念。打个比方,这相当于是说:朋友是值得信任的,所以可以对他们公开一些自己的隐私。
友元分为两种:友元函数和友元类。
1. 友元函数
类的友元函数是定义在类外部,但有权访问类的所有私有(private
)成员和保护(protected
)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。
在定义一个类的时候,可以把一些函数(包括全局函数和其他类的成员函数)声明为“友元”,这样那些函数就成为该类的友元函数,在友元函数内部就可以访问该类对象的私有成员了。
将全局函数声明为友元的写法如下:
friend 返回值类型 函数名(参数表);
将其他类的成员函数声明为友元的写法如下:
friend 返回值类型 其他类的类名::成员函数名(参数表);
但是,不能把其他类的私有成员函数声明为友元函数。
示例代码如下:
#include <iostream>
using namespace std;
class Student
{
public:
Student(string i, string n, int a); // 简单的构造函数
~Student(); // 析构函数
string getID();
friend int getAge(Student stu); // 友元函数
private:
string id;
string name;
int age;
};
Student::Student(string i="", string n="", int a=0)
{
cout << "constructor func run" << endl;
id = i;
name = n;
age = a;
}
Student::~Student()
{
cout << "destructor func run" << endl;
}
string Student::getID()
{
return id;
}
// getAge() 不是任何类的成员函数
int getAge(Student stu)
{
// 因为 getAge() 是 Student 的友元函数,所以它可以直接访问该类的任何成员 */
return stu.age;
}
int main()
{
Student stu ("0001", "Jack", 18);
// 使用友元函数获取年龄
int age = getAge(stu);
cout << "age is " << age << endl;
return 0;
}
输出结果:
constructor func run
destructor func run
age is 18
destructor func run
因为友元函数没有 this
指针,则参数要有三种情况:
- 要访问非
static
成员时,需要对象做参数; - 要访问
static
成员或全局变量时,则不需要对象做参数; - 如果做参数的对象是全局对象,则不需要对象做参数;
- 可以直接调用友元函数,不需要通过对象或指针;
示例代码:
class INTEGER
{
friend void Print(const INTEGER& obj);//声明友元函数
};
void Print(const INTEGER& obj)
{
//函数体
}
void main()
{
INTEGER obj;
Print(obj);//直接调用
}
2. 友元类
友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类,在这种情况下,整个类及其所有成员都是友元。
一个类 A
可以将另一个类 B
声明为自己的友元,类 B
的所有成员函数就都可以访问类 A
对象的私有成员。在类定义中声明友元类的写法如下:
friend class 类名;
示例代码:
#include <iostream>
using namespace std;
class Student
{
public:
Student(string i, string n, int a); // 简单的构造函数
string getID();
friend class Teacher; // 友元函数
private:
string id;
string name;
int age;
};
Student::Student(string i="", string n="", int a=0)
{
cout << "constructor func run" << endl;
id = i;
name = n;
age = a;
}
class Teacher
{
public:
void checkStudent(Student stu);
};
void Teacher::checkStudent(Student stu)
{
// Teacher 是 Student 的友元类,它可以直接访问 Student 类的任何成员
cout << "sut.age is " << stu.age << endl;
cout << "sut.name is " << stu.name << endl;
}
int main()
{
Student stu ("0001", "Jack", 18);
Teacher t;
t.checkStudent(stu);
return 0;
}
输出结果:
constructor func run
sut.age is 18
sut.name is Jack
友元关系在类之间不能传递,即类 A
是类 B
的友元,类 B
是类 C
的友元,并不能导出类 A
是类 C
的友元。“咱俩是朋友,所以你的朋友就是我的朋友”这句话在 C++
的友元关系上不成立,当然在现实生活中很大概率也是不能成立的。
我们使用了友元之后,发现在设计程序的时候方便了很多。原先的那些私有成员都能轻松地被访问了。于是我们不用去写那些繁琐的成员函数,程序执行的时候也减少了函数的调用次数,提高了运行效率。
友元的存在,破坏了类的封装性。一个类出现问题,就不仅仅是由这个类本身负责了,还可能和它众多的友元有关。这无疑使得检查调试的范围突然扩大了许多,难度也陡然增加。
所以,我们在使用友元的时候,权衡使用友元的利弊,使程序达到最佳的效果。