引言:

「 友元(friend)」是一个用以帮助其他类中的成员函数以及全局范围的函数访问当前类的 private 和 protected 成员的机制。

【C++ 学习总结】- 03 - 类:友元关系

  • 一、友元的概念
    • 1. 基本概念
    • 2. 友元的特性
  • 二、友元的使用
    • 1. 友元的声明
    • 2. 示例1:非成员函数声明为友元函数
    • 3. 示例2:成员函数声明为友元函数
    • 4. 示例3:类声明为友元类

一、友元的概念

1. 基本概念

  「 友元(friend)」 是一个用以帮助其他类中的成员函数以及全局范围的函数访问当前类的 privateprotected 成员的机制。使用友元机制的可以是类,也可以是不属于本类的函数,但无一例外都需要先在要建立 “friend” 关系的目标类里使用 <friend> 关键字先进行友元声明。声明为友元函数后,函数可以访问本不可以访问的类的 privateprotected 成员,而友元类中的每个成员函数都可以访问建立了友元关系的目标类的所有成员。

  对于 <friend> 这个词我们可以这么理解:对外人我们会保持警惕、潜意识里自我保护,但是和亲密的朋友就会敞开心扉、分享秘密,而对于类来说友元关系就是其分享本为私密的成员变量给声明为友元的对象。

《挑战30天C/C++》 中这样说道:
  友元能够使得普通函数直接访问类的保护数据,避免了类成员函数的频繁调用,可以节约处理器开销,提高程序的效率。但所矛盾的是,即使是最大限度的保护,同样也破坏了类的封装特性,这即是友元的缺点。在现在cpu速度越来越快的今天我们并不推荐使用它,但它作为 C++ 的一个必要的知识点,一个完整的组成部分,我们还是需要讨论一下的。

2. 友元的特性

  • 友元在不破坏类的整体封装性的前提下,向外部函数特殊地提供了其内部访问权,以实现某些特殊功能。

  • 友元定义在外部,但是需要在类的内部进行友元声明,从而确定要建立友元关系的目标类。

  • 友元是单向的,类A在类B中声明了友元 friend class A;,那么A可以访问B,但是B不能访问A

  • 友元关系不能被传递。附庸的附庸不是我的附庸,友元的友元不是我的友元。

  • 友元关系不能被继承,因为友元并不是类的成员。

  • 友元不是类的成员,不受类的访问权限关键字 publicprotectedprivate 的修饰。

  • 友元不是类的成员,没有 this 指针,所以不能访问类的非 static 成员。因此,需要在其参数中手动地指明目标对象。

  • 友元不是类的成员,因此无需通过对象而可以直接调用。

二、友元的使用

1. 友元的声明

  通过在目标类中使用 <friend> 关键字进行声明来使函数或类成为目标类的友元函数友元类,声明方法为在原声明语句前添加一个关键字 <friend>,其语法格式如下:

 // 声明友元关系class classA {access specifier:// member variables// member functionsfriend 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);               // 声明非成员函数为友元函数
};/* show 的实现 */
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;      // 预先声明类 Student, 以定义参数 'Student *pstu'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);       // 声明成员函数 checkvoid teach (Student *pstu);      // 声明成员函数 teach
};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);       // 声明别的类的成员函数为友元函数
};/* show 的实现 */
void show (Student *pstu) {printf("-> Name: %s,  Age: %d,  Score: %.2f \n", pstu->m_name, pstu->m_age, pstu->m_score);
}/* check 的实现 */
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);}
}/* teach 的实现 */
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;      // 预先声明类 Student, 以定义参数 'Student *pstu'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);       // 声明成员函数 checkvoid teach (Student *pstu);      // 声明成员函数 teach
};class Student {private:const char * m_name;       // 姓名int         m_age;         // 年龄float       m_score;       // 分数friend class Teacher;      // 声明友元类 Teacherpublic: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);                // 声明非成员函数为友元函数
};/* show 的实现 */
void show (Student *pstu) {printf("-> Name: %s,  Age: %d,  Score: %.2f \n", pstu->m_name, pstu->m_age, pstu->m_score);
}/* check 的实现 */
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);}
}/* teach 的实现 */
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

  —— DaveoCKII
2022.05.04

【C++ 学习总结】- 03 - 类的认识:友元关系相关推荐

  1. 友元函数、友元类、访问私有数据成员、友元关系[C++]

    友元函数(friend function) 1. 什么是友元函数?     一个类的私有数据成员通常只能由类的函数成员来访问,而友元函数可以访问类的私有数据成员,也能访问其保护成员 2. 友元函数的用 ...

  2. 《Unity API常用方法和类详细讲解—Siki学院》课程学习笔记03

    <Unity API常用方法和类详细讲解-Siki学院>课程学习笔记03 课时18-20协程及其执行 1.使用Coroutine实现颜色动画渐变 void Update(){if (Inp ...

  3. C++模板学习02(类模板)(类模板语法、类模板与函数模板的区别、类模板中的成员函数创建时机、类模板对象做函数参数、类模板与继承、类模板成员函数类外实现、类模板分文件编写、类模板与友元)

    C++引用详情(引用的基本语法,注意事项,做函数的参数以及引用的本质,常量引用) 函数高级C++(函数的默认参数,函数的占位参数,函数重载的基本语法以及注意事项) C++类和对象-封装(属性和行为作为 ...

  4. C++学习第三天——类的组合+友元函数+常类型+动态内存分配

    类和组合 对象数组和对象指针 可以进行定义对象数组和对象指针 一维对象数组定义方法 类名 数组名[下标表达式]; 类名 数组名[下标表达式]={类名(-),类名(-)}; //第一种默认调用系统给的无 ...

  5. 模板 (函数模板语法 ,类模板与函数模板的区别,:函数模板案例,普通函数与函数模板的区别,普通函数与函数模板调用规则,模板的局限性,类模板分文件编写.cpp,Person.hpp,类模板与友元)

    **01:函数模板语法: #include<iostream> using namespace std;//交换两个整型函数 void swapInt(int &a ,int &a ...

  6. java中友元类_友元类成员的依赖关系|循环依赖

    定义一个CBottle类,另一个类CCarton的某个成员对CBottle进行操作,因此在CBottle类中赋予CCarton成员的友元权利.我们很容易写出如下代码: //CBottle类的头文件 b ...

  7. HTML/CSS学习笔记03【CSS概述、CSS选择器、CSS属性、CSS案例-注册页面】

    w3cschool菜鸟教程.CHM(腾讯微云):https://share.weiyun.com/c1FaX6ZD HTML/CSS学习笔记01[概念介绍.基本标签.表单标签][day01] HTML ...

  8. JDBC学习笔记03【JDBC事务管理、数据库连接池、JDBCTemplate】

    黑马程序员-JDBC文档(腾讯微云)JDBC笔记.pdf:https://share.weiyun.com/Kxy7LmRm JDBC学习笔记01[JDBC快速入门.JDBC各个类详解.JDBC之CR ...

  9. java/03/类与对象,深入分析类与对象(权限修饰,构造方法和简单java类),数组的定义及使用

    java/03/类与对象,深入分析类与对象(权限修饰,构造方法和简单java类),数组的定义及使用 七十年代,IBM的Smalltalk语言最先推广面向对象,后来C语言变为C++,后来C++又产生了J ...

最新文章

  1. while循环的习题
  2. Qt5: SpringAnimation
  3. C语言--指针函数和函数指针
  4. Ubuntu 下常用的命令 简略记录
  5. 优化案例(part1)--Efficient multi-modal geometric mean metric learning
  6. Java学习指导————如何做到基础扎实
  7. linux sftp密码错误,linux个别用户sftp坏掉,验证密码后卡住, 大概是什么问题?...
  8. Ubuntu中Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend)问题的解决
  9. 小猿圈python_小猿圈python之内置方法new
  10. 考研数学 曲线曲面积分
  11. PHP、Java、Python、C、C++ 这几种编程语言都各有什么特点或优点
  12. 移动端抓包工具spy-debugger使用
  13. 使用html+css3画一个波士顿凯尔特人的三叶草队标
  14. 智能手环,智能手表,智能眼镜,我们到底该怎么选呢?
  15. SpringBoot整合Shiro搭建登录注册认证授权权限项目模板
  16. 作业成本法中的成本动因分析----by AMT 邓为民
  17. C语言小项目-火车票订票系统
  18. 计算机重启后一直黑屏转圈,技术员教你解决win10开机无限黑屏转圈重启的解决方法...
  19. 3D游戏引擎系列十一
  20. Latex, overleaf 输入中文 可行方法

热门文章

  1. 【Rational Rose使用笔记】协作图
  2. python怎么打开spyder_Python开发环境Spyder安装方法
  3. 高德地图使用坑点记录。
  4. c语言浮点型与整形比较大小,C语言整形与浮点型转化过程中的精度损失
  5. 机器名改动引起的SQL Server 2005的26,40 or 53错误一则
  6. vivo Y55的Usb调试模式在哪里,打开vivo Y55Usb调试模式的步骤
  7. 单元测试报错:org.junit.platform.commons.util.ReflectionUtils.getDefaultClassLoader()Ljava/lang/ClassLoader
  8. 注意力之双线性模型注意力
  9. 【机器学习】--隐语义模型
  10. c语言字母可以加减吗,C语言算式加减法运算