彻底说清楚友元

一般来说,类的私有成员只能在类的内部访问,类之外是不能访问它们的。但如果将其他类/函数设置为类的友元,那么友元类/函数就可以在前一个类的类定义之外访问其私有成员了。用friend关键字声明友元

将类比作一个家庭,类的 private 成员相当于家庭的秘密,一般的外人当然不允许探听这些秘密的,只有 friend 才有资格探听这些秘密。

友元的三种形式:普通函数、成员函数、友元类

友元之普通函数形式

1
2
3
4
5
6
7
8
9
10
11
12
13
class Student {
public:
Student(int age,const string& name):m_age(age),m_name(name){}
private:
int m_age;
string m_name;
};

void access() {
Student stu(12, "xy");

// 无法访问 任何 stu 的任何成员
}

普通函数 access 创建 stu 对象,但由于成员为私有,导致无法访问。

在 Student 类中将该函数的定义放入其中,就可以成功访问到了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Student {
friend void access(); // 普通函数声明为友元
public:
Student(int age,const string& name):m_age(age),m_name(name){}
private:
int m_age;
string m_name;
};

void access() {
Student stu(12, "xy");

cout << "age = " << stu.m_age << " name = " << stu.m_name << endl;
}

友元之成员函数形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Student {
public:
Student(int age,const string& name):s_age(age),s_name(name){}
private:
int s_age;
string s_name;
};

class Teacher {
public:
Teacher(int age, const string& name) :t_age(age), t_name(name) {}
void accessStu() {
Student stu(13, "xy");
//cout << "age = " << stu.s_age << " name = " << stu.s_name << endl; 失败
}
private:
int t_age;
string t_name;
};

类 Teacher 企图访问另一个类 Student 的私有成员,同样不可能访问成功。

将类 Teacher 的成员函数 accessStu 声明为 类 Student 友元成员函数即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Teacher {
public:
Teacher() = default;
void accessStu();
};

class Student {
friend void Teacher::accessStu();
public:
Student(int age,const string& name):s_age(age),s_name(name){}
private:
int s_age;
string s_name;
};

void Teacher::accessStu() {
Student stu(13, "xy");
cout << "age = " << stu.s_age << " name = " << stu.s_name << endl;
}

友元类

类 Teacher 要访问 类 Student,需要由类 Student 在其中将 类 Teacher声明为自己的友元类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Student {
friend class Teacher; // 从此 类Teacher生成的对象,可以访问 Student 私有数据
public:
Student(int age,const string& name):s_age(age),s_name(name){}
private:
int s_age;
string s_name;
};

class Teacher {
public:
Teacher() = default;
void accessStu() {
Student stu(13, "xy");
cout << "age = " << stu.s_age << " name = " << stu.s_name << endl;
}
};

友元的特点

  1. 友元不受类中访问权限的限制——可访问私有成员
  2. 友元破坏了类的封装性,不能滥用友元
  3. 友元是单向的——A类是B类的友元类,则A类成员函数中可以访问B类私有成员;但并不代表B类是A类的友元类,如果A类中没有声明B类为友元类,此时B类的成员函数中并不能访问A类私有成员
  4. 友元不具备传递性——A是B的友元类,B是C的友元类,无法推断出A是C的友元类
  5. 友元不能被继承——因为友元破坏了类的封装性,为了降低影响,设计层面上友元不能被继承