【C++ 学习总结】- 03 - 类:友元类和友元函数
一、友元的概念
1. 基本概念
??「 友元(friend)」 是一个用以帮助其他类中的成员函数以及全局范围的函数访问当前类的 private 和 protected 成员的机制。使用友元机制的可以是类,也可以是不属于本类的函数,但无一例外都需要先在要建立 “friend” 关系的目标类里使用 <friend> 关键字先进行友元声明。声明为友元函数后,函数可以访问本不可以访问的类的 private 和 protected 成员,而友元类中的每个成员函数都可以访问建立了友元关系的目标类的所有成员。
??对于 <friend> 这个词我们可以这么理解:对外人我们会保持警惕、潜意识里自我保护,但是和亲密的朋友就会敞开心扉、分享秘密,而对于类来说友元关系就是其分享本为私密的成员变量给声明为友元的目标。
《挑战30天C/C++》 中这样说道: ??友元能够使得普通函数直接访问类的保护数据,避免了类成员函数的频繁调用,可以节约处理器开销,提高程序的效率。但所矛盾的是,即使是最大限度的保护,同样也破坏了类的封装特性,这即是友元的缺点。在现在cpu速度越来越快的今天我们并不推荐使用它,但它作为 C++ 的一个必要的知识点,一个完整的组成部分,我们还是需要讨论一下的。
2. 友元的特性
-
友元在不破坏类的整体封装性的前提下,向外部函数特殊地提供了其内部访问权,以实现某些特殊功能。 -
友元定义在外部,但是需要在类的内部进行友元声明,从而确定要建立友元关系的目标类。 -
友元是单向的,类A在类B中声明了友元 friend class A; ,那么A可以访问B,但是B不能访问A -
友元关系不能被传递。附庸的附庸不是我的附庸,友元的友元不是我的友元。 -
友元关系不能被继承,因为友元并不是类的成员。 -
友元不是类的成员,不受类的访问权限关键字 public、protected、private 的修饰。 -
友元不是类的成员,没有 this 指针,所以不能访问类的非 static 成员。因此,需要在其参数中手动地指明目标对象。 -
友元不是类的成员,因此无需通过对象而可以直接调用。
二、友元的使用
1. 友元的声明
??通过在目标类中使用 <friend> 关键字进行声明来使函数或类成为目标类的友元函数、友元类,声明方法为在原声明语句前添加一个关键字 <friend>,其语法格式如下:
class classA {
access specifier:
friend class classB;
friend varType funcName (parameters);
}
??经过声明的友元函数即可访问类中的所有成员。需要注意的是,如果访问的不是 static 成员,就需要在参数中指明目标对象。我们知道,成员函数实际上有一个隐式的 this 指针指向当前对象,用于访问对象的成员,而友元函数并不是本类的成员函数,也就没有 this 指针,无从访问对象的成员,所以我们就需要手动地为其指明要访问的对象。 ??经过声明的友元类的所有成员函数都可以访问目标类中的所有成员,这种方法好在可以一次性地对所有成员函数完成友元声明而不必再一次次地进行繁杂重复的声明工作,坏处是增加了类的封装性被破坏的风险。因此,一般不建议将整个类声明为友元。
2. 示例1:非成员函数声明为友元函数
================================ 定义示例 ================================
class Student {
private:
const char * m_name;
int m_age;
float m_score;
public:
Student (const char * name, int age, float score) :
m_name(name),
m_age(age),
m_score(score)
{
printf("-> 学生 %s 已添加 \n", name);
}
friend void show (Student *pstu);
};
void show (Student *pstu) {
printf("-> Name: %s, Age: %d, Score: %.2f \n", pstu->m_name, pstu->m_age, pstu->m_score);
}
================================ 程序示例 ================================
int main ()
{
Student stu("张三", 16, 82.5f);
show(&stu);
return 0;
}
================================ 运行结果 ================================
-> 学生 张三 已添加
-> Name: 张三, Age: 16, Score: 82.50
3. 示例2:成员函数声明为友元函数
??不仅可以声明非成员函数为友元函数,别的类的成员函数也可以声明为本类的友元函数,以访问本类的非公成员。
================================ 定义示例 ================================
class Student;
class Teacher {
private:
const char * m_name;
int m_age;
public:
Teacher (const char * name, int age) :
m_name(name),
m_age(age)
{
printf("-> 老师 %s 已添加 \n", name);
}
void check (Student *pstu);
void teach (Student *pstu);
};
class Student {
private:
const char * m_name;
int m_age;
float m_score;
public:
Student (const char * name, int age, float score) :
m_name(name),
m_age(age),
m_score(score)
{
printf("-> 学生 %s 已添加 \n", name);
}
friend void show (Student *pstu);
friend void Teacher::check (Student *pstu);
friend void Teacher::teach (Student *pstu);
};
void show (Student *pstu) {
printf("-> Name: %s, Age: %d, Score: %.2f \n", pstu->m_name, pstu->m_age, pstu->m_score);
}
void Teacher::check (Student *pstu) {
if((pstu->m_score > 60)) {
printf("-> 学生 %s 成绩合格, 当前成绩: %.2f \n", pstu->m_name, pstu->m_score);
}
else {
printf("-> 学生 %s 成绩不合格, 当前成绩: %.2f \n", pstu->m_name, pstu->m_score);
}
}
void Teacher::teach (Student *pstu) {
if(pstu->m_score < 70) {
pstu->m_score += 10;
printf("-> %s 的教学效果显著, %s 的成绩大幅提升! \n", m_name, pstu->m_name);
}
else if (pstu->m_score < 90) {
pstu->m_score += 5;
printf("-> %s 的教学效果不错, %s 的成绩小幅提升! \n", m_name, pstu->m_name);
}
}
================================ 程序示例 ================================
int main ()
{
Student stu("张三", 16, 82.5f);
show(&stu);
Teacher tch("张老师", 26);
tch.teach(&stu);
tch.check(&stu);
return 0;
}
================================ 运行结果 ================================
-> 学生 张三 已添加
-> Name: 张三, Age: 16, Score: 82.50
-> 老师 张老师 已添加
-> 张老师 的教学效果不错, 张三 的成绩小幅提升!
-> 学生 张三 成绩合格, 当前成绩: 87.50
4. 示例3:类声明为友元类
??在 示例2 中我们在类 Student 中声明了两个 Teacher 类中的成员函数为友元函数,从而让它们可以访问 Student 中的私有成员,在这里我们将换一种方法,通过声明友元类来实现完全相同的效果。
================================ 定义示例 ================================
class Student;
class Teacher {
private:
const char * m_name;
int m_age;
public:
Teacher (const char * name, int age) :
m_name(name),
m_age(age)
{
printf("-> 老师 %s 已添加 \n", name);
}
void check (Student *pstu);
void teach (Student *pstu);
};
class Student {
private:
const char * m_name;
int m_age;
float m_score;
friend class Teacher;
public:
Student (const char * name, int age, float score) :
m_name(name),
m_age(age),
m_score(score)
{
printf("-> 学生 %s 已添加 \n", name);
}
friend void show (Student *pstu);
};
void show (Student *pstu) {
printf("-> Name: %s, Age: %d, Score: %.2f \n", pstu->m_name, pstu->m_age, pstu->m_score);
}
void Teacher::check (Student *pstu) {
if((pstu->m_score > 60)) {
printf("-> 学生 %s 成绩合格, 当前成绩: %.2f \n", pstu->m_name, pstu->m_score);
}
else {
printf("-> 学生 %s 成绩不合格, 当前成绩: %.2f \n", pstu->m_name, pstu->m_score);
}
}
void Teacher::teach (Student *pstu) {
if(pstu->m_score < 70) {
pstu->m_score += 10;
printf("-> %s 的教学效果显著, %s 的成绩大幅提升! \n", m_name, pstu->m_name);
}
else if (pstu->m_score < 90) {
pstu->m_score += 5;
printf("-> %s 的教学效果不错, %s 的成绩小幅提升! \n", m_name, pstu->m_name);
}
}
================================ 程序示例 ================================
int main ()
{
Student stu("张三", 16, 82.5f);
show(&stu);
Teacher tch("张老师", 26);
tch.teach(&stu);
tch.check(&stu);
return 0;
}
================================ 运行结果 ================================
-> 学生 张三 已添加
-> Name: 张三, Age: 16, Score: 82.50
-> 老师 张老师 已添加
-> 张老师 的教学效果不错, 张三 的成绩小幅提升!
-> 学生 张三 成绩合格, 当前成绩: 87.50
|