C++面向对象编程之五:友元(friend)
C++中,允许一个类的非共有成员被这个类授予友元(friend)关系的全局函数,另一个类,或另一个类中的成员函数访问。友元不是一个类中的成员,所以它们不受声明出现部分的访问权限(public,protected,private)影响。
友元函数
友元函数是在类中用关键字friend修饰的非成员函数。友元函数可以是一个普通的函数,也可以是其他类的成员函数。虽然它不是本类的成员函数,但是在它的函数体中可以通过对象名访问类的私有和保护成员。
友元类
友元类是在类中用关键字friend修饰的另一个类的声明。那么这个友元类的所有成员函数都是这个类的友元函数,在友元类的成员函数体内都可以通过对象名访问这个类的私有成员和保护成员。
语法:
全局函数做友元
void spell(){}
class Monster
{ //全局函数做友元friend void spell();
};
类做友元(友元类)
class Skill{};
class Monster
{//友元类friend class Skill;
};类的成员函数做友元
class Skill
{void spell(){}
};
class Monster
{//类的成员函数做友元friend void Skill::spell();
};example:全局函数做Monster类的友元
#include <iostream>
using namespace std;enum SKILL_TYPE
{BLOOD = 0
};class Monster;//施法
void spellSkill(Monster &src, Monster &dest, const SKILL_TYPE skillType, const int skillVal);class Monster
{friend void spellSkill(Monster &src, Monster &dest, const SKILL_TYPE skillType, const int skillVal); //全局函数做友元public:Monster():m_monsterId(0), m_name("怪物"), m_blood(0){}Monster(const int monsterId, const string name, const int blood):m_monsterId(monsterId), m_name(name), m_blood(blood){}Monster(const Monster &m):m_monsterId(m.m_monsterId), m_name(m.m_name), m_blood(m.m_blood){}~Monster(){}void spell(Monster &dest){spellSkill(*this, dest, BLOOD, 1000);}private:int m_monsterId; //怪物idstring m_name; //怪物名字int m_blood; //血量
};void spellSkill(Monster &src, Monster &dest, const SKILL_TYPE skillType, const int skillVal)
{switch (skillType){case BLOOD:{int blood = 0;//destdest.m_blood -= skillVal; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的非共有属性if (dest.m_blood < 0)dest.m_blood = 0;//srcsrc.m_blood += skillVal; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_blood//因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_namecout << src.m_name << " 攻击了 " << dest.m_name << endl; cout << src.m_name << "的血量增加到:" << src.m_blood << endl;cout << dest.m_name << "的血量减少到 " << dest.m_blood << endl;break;}default:cout << "技能类型未处理:" << skillType << endl;}
}int main(int argc, char *argv[])
{Monster m1(10001, "雪女", 10000);Monster m2(10001, "紫衣仙子", 20000);m1.spell(m2);return 0;
}
example:Skill类做Monster类的友元
#include <iostream>
using namespace std;enum SKILL_TYPE
{BLOOD = 0
};class Monster;class Skill
{public:Skill():m_skillType(BLOOD), m_val(500){}Skill(const int skillType, const int val):m_skillType(skillType), m_val(val){}Skill(const Skill &s):m_skillType(s.m_skillType), m_val(s.m_val){}~Skill(){}//施法void spell(Monster &src, Monster &dest);private:int m_skillType; //技能类型int m_val;
};class Monster
{friend class Skill; //友元类public:Monster():m_monsterId(0), m_name("怪物"), m_blood(0), m_skill(BLOOD, 1000){}Monster(const int monsterId, const string name, const int blood, const int skillType, const int skillVal):m_monsterId(monsterId), m_name(name), m_blood(blood), m_skill(skillType, skillVal){}Monster(const Monster &m):m_monsterId(m.m_monsterId), m_name(m.m_name), m_blood(m.m_blood), m_skill(m.m_skill){}~Monster(){}void spell(Monster &dest){m_skill.spell(*this, dest);}private:int m_monsterId; //怪物idstring m_name; //怪物名字int m_blood; //血量Skill m_skill; //技能
};//施法
void Skill::spell(Monster &src, Monster &dest)
{switch (m_skillType){case BLOOD:{int blood = 0;//destdest.m_blood -= m_val; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的非共有属性if (dest.m_blood < 0)dest.m_blood = 0;//srcsrc.m_blood += m_val; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_blood//因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_namecout << src.m_name << " 攻击了 " << dest.m_name << endl; cout << src.m_name << "的血量增加到:" << src.m_blood << endl;cout << dest.m_name << "的血量减少到 " << dest.m_blood << endl;break;}default:cout << "技能类型未处理:" << m_skillType << endl;}
}int main(int argc, char *argv[])
{Monster m1(10001, "雪女", 10000, BLOOD, 1000);Monster m2(10001, "紫衣仙子", 20000, BLOOD, 1000);m1.spell(m2);return 0;
}
example:Skill类的成员函数做Monster类的友元
#include <iostream>
using namespace std;enum SKILL_TYPE
{BLOOD = 0
};class Monster;class Skill
{public:Skill():m_skillType(BLOOD), m_val(500){}Skill(const int skillType, const int val):m_skillType(skillType), m_val(val){}Skill(const Skill &s):m_skillType(s.m_skillType), m_val(s.m_val){}~Skill(){}//施法void spell(Monster &src, Monster &dest);private:int m_skillType; //技能类型int m_val;
};class Monster
{friend void Skill::spell(Monster &src, Monster &dest); //Skill类的成员函数做友元public:Monster():m_monsterId(0), m_name("怪物"), m_blood(0), m_skill(BLOOD, 1000){}Monster(const int monsterId, const string name, const int blood, const int skillType, const int skillVal):m_monsterId(monsterId), m_name(name), m_blood(blood), m_skill(skillType, skillVal){}Monster(const Monster &m):m_monsterId(m.m_monsterId), m_name(m.m_name), m_blood(m.m_blood), m_skill(m.m_skill){}~Monster(){}void spell(Monster &dest){m_skill.spell(*this, dest);}private:int m_monsterId; //怪物idstring m_name; //怪物名字int m_blood; //血量Skill m_skill; //技能
};//施法
void Skill::spell(Monster &src, Monster &dest)
{switch (m_skillType){case BLOOD:{int blood = 0;//destdest.m_blood -= m_val; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的非共有属性if (dest.m_blood < 0)dest.m_blood = 0;//srcsrc.m_blood += m_val; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_blood//因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_namecout << src.m_name << " 攻击了 " << dest.m_name << endl; cout << src.m_name << "的血量增加到:" << src.m_blood << endl;cout << dest.m_name << "的血量减少到 " << dest.m_blood << endl;break;}default:cout << "技能类型未处理:" << m_skillType << endl;}
}int main(int argc, char *argv[])
{Monster m1(10001, "雪女", 10000, BLOOD, 1000);Monster m2(10001, "紫衣仙子", 20000, BLOOD, 1000);m1.spell(m2);return 0;
}
为什么要用友元
如果需要在某个全局函数,某一个类或某一个类中的成员函数访问另一个类的私有或保护成员变量,又要求提高代码的执行效率,减少系统开销,我们可以选择让某个全局函数,某一个类或某一个类中的成员函数为另一个类的友元(friend),但这会破坏另一个类的封装性。所以在实际的开发过程中,我们应该按照实际需求选择是否用友元。
友元的特性
单向性:比如上面的例子中,Skill类是Monster类的友元,但Monster类不是Skill类的友元
友元不能被继承:比如上面的例子中Skill类是Monster类的友元,假如SceneSkill类是Skill类的子类,SceneSkill类不是Monster类的友元。
一般情况下,用友元函数重载<<,>>操作符
相关文章:
C++面向对象编程之五:友元(friend)
C中,允许一个类的非共有成员被这个类授予友元(friend)关系的全局函数,另一个类,或另一个类中的成员函数访问。友元不是一个类中的成员,所以它们不受声明出现部分的访问权限(public,p…...
[手写OS]动手实现一个OS 之X86实模式下的汇编开发
[手写OS]动手实现一个OS 之X86实模式下的汇编开发 x86实模式下 汇编开发是一个 intel x86实模式中的汇编程序开发类型。它涉及操纵几个16位处理器寄存器,并仅处理内存中的物理地址(与受保护模式相对)。 这种类型的编程中最广为人知的应用就…...
【Linux内核二】常用的网络丢包错包debug工具介绍
目录 ifconfig Ifconfig输出各字段简述 txqueuelen RX和TX的errors指哪些错误 dropped与overruns的区别 常用ifconfig配置命令 显示网卡信息 启动关闭指定网卡 配置和删除ip地址 修改MAC地址 启用和关闭ARP协议 设置最大传输单元 设置网卡的promiscuous模式 设置…...
qt控件增加渐变色效果
ui->returnBtn->setStyleSheet("color: rgb(0, 0, 0);""background:qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, ""stop:0 #5f5f5f, stop:0.5 #ffffff, stop:0.98 #5f5f5f);""border:none;");效果如下图: …...
【node : 无法将“node”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。 最全面有效的解决方案】
执行nodejs文件错误: 这个错误提示通常是由于你的系统无法识别 "node" 命令,可能是由于你没有正确地安装或配置 Node.js 环境变量。 问题描述 原因分析: 可能原因包括: 1.Node.js未正确安…...
打怪升级之字符串的分界符与字符串替换
流的字符串分界符 在C的iostream中,有流的字符串分界符: " “和”"都代表简单的分隔。 因此,使用流来做字符串分隔的话,有一个比较简单的方案就是将原定义的分隔符通过替换的方式变成流的分隔符。然后再录入流中就能…...
载荷台子使用方式
载荷自动测量台子使用方法 电源开关旋转到1,开启电源开启台子微机开关,开启电脑(winxp)开启台子载荷开关,启动载荷台子点击电脑动标图标,开启软件放入载荷,弹性体向上,载荷台子压头压…...
1005 继续(3n + 1)猜想
卡拉兹(Callatz)猜想已经在1001中给出了描述。在这个题目里,情况稍微有些复杂。 当我们验证卡拉兹猜想的时候,为了避免重复计算,可以记录下递推过程中遇到的每一个数。例如对 n3 进行验证的时候,我们需要计算 3、5、8、4、2、1&a…...
VMware15配置NAT模式联通网络
最近为了测试C# 开发的桌面应用程序悬浮球的兼容性,在虚拟机上安装了win7系统和xp系统,之前也安装过黑苹果系统,但是win系统倒是第一次安装,在win7系统联网的时候,踩了一些坑,整理纪录一下。 设置主物理机配…...
doPost的实际使用
目录 前言 一、doPost是什么? 二、使用步骤 1.doPost的请求方法 2.需要引入依赖 总结 前言 本章主要记录一下doPost的请求公用方法的使用。 一、doPost是什么? 它其实就是一个http的post请求方式。 二、使用步骤 1.doPost的请求方法 当我们系…...
2017年MathorCup数学建模A题流程工业的智能制造解题全过程文档及程序
2017年第七届MathorCup高校数学建模挑战赛 A题 流程工业的智能制造 原题再现: “中国制造 2025”是我国制造业升级的国家大战略。其技术核心是智能制造,智能化程度相当于“德国工业 4.0”水平。“中国制造 2025”的重点领域既包含重大装备的制造业&…...
HNU-电子测试平台与工具2-数模转换
数模转换实验 计科XXXX wolf 工程文件我也一并上传了 D级任务 一.实验任务 对74194进行仿真验证,掌握Quartus仿真的基本原则和常规步骤,记录移位寄存器的数据读写,并描述仿真波形,分析结果。 二.实验过程 1.电路连接 2.功能…...
CentOS7安装Telnet客户端和服务端和使用方式
在执行telnet时会提示命令不存在。Telnet服务的配置步骤如下:一、检测是否安装telnet软件包(通常要两个)1、telnet-client (或 telnet),这个软件包提供的是 telnet 客户端程序;2、telnet-server 软件包,这个才是真正的…...
脂肪毒性的新兴调节剂——肠道微生物组
谷禾健康 肠道微生物组与脂质代谢:超越关联 脂质在细胞信号转导中起着至关重要的作用,有助于细胞膜的结构完整性,并调节能量代谢。 肠道微生物组通过从头生物合成和对宿主和膳食底物的修饰产生了大量的小分子。 最近的研究表明,由…...
【JavaSE系列】 第九节 —— 多态那些事儿
文章目录 前言 一、多态的概念 二、向上转型和向下转型 2.1 向上转型 2.2 什么是向上转型 2.3 三种常见的向上转型 2.3.1 直接赋值 2.3.2 作为方法的参数 2.3.3 作为方法的返回值 2.4 向下转型(这个了解即可) 三、方法重写 3.1 方法重写的…...
ego微商小程序项目-测试步骤
文章目录 1. 需求分析和评审2. 编写测试计划和测试方案2.1 ego小程序测试计划2.1.1 项目简介2.1.2 项目任务2.1.3 项目风险2.1.4 测试方案2.1.5 测试实施2.1.6 测试管理2.1.7 附录资料3. 编写测试用例和评审3.1 功能测试用例设计3.1.1 总-整体把控3.1.2 分- 拆分细化3.2 测试用…...
华为OD机试用Python实现 -【报数游戏】2023Q1 A卷
华为OD机试题 本篇题目:报数游戏题目输入输出示例 1输入输出示例 2输入输出Code代码编写思路最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解...
Plsql使用
登录登录system用户,初始有两个用户sys和system,密码是自己安装oracle数据库时写的,数据库选择orcl创建用户点击user,右键新增填写权限关于3个基本去权限介绍: connect : 基本操作表的权限,比如增删改查、视图创建等 r…...
小丑改造计划之四线程控制
1.线程有哪些优点,缺点? 1.优点: 创建线程的代价比较小 线程切换比进程的切换,操作系统要做的事少 线程比进程占用的资源要少 缺点: 子线程可能会影响主线程,健壮性不如进程 编写多线程比单线程难ÿ…...
Spring注册Bean的几种方式
通过XML配置注册Bean spring-config.xml <!--方式一:声明自定义的bean,没有设置id,id默认为全类型#编号--><bean id"cat" class"com.rzg.entity.Cat"/><bean class"com.rzg.entity.Cat"/>public class SpringApp…...
Wan2.2-I2V-A14B镜像部署教程:无需conda/pip,纯脚本一键启动
Wan2.2-I2V-A14B镜像部署教程:无需conda/pip,纯脚本一键启动 1. 镜像概述与核心优势 Wan2.2-I2V-A14B是一款专为文生视频任务优化的私有部署镜像,特别针对RTX 4090D 24GB显存显卡进行了深度优化。这个镜像的最大特点是开箱即用,…...
3大核心模块构建戴森球计划模块化生产体系:从混乱到有序的进阶指南
3大核心模块构建戴森球计划模块化生产体系:从混乱到有序的进阶指南 【免费下载链接】FactoryBluePrints 游戏戴森球计划的**工厂**蓝图仓库 项目地址: https://gitcode.com/GitHub_Trending/fa/FactoryBluePrints 概念解析:模块化生产的本质与价值…...
AI Agent开发实战路线图:从入门到企业级应用的4阶段进阶指南
第一阶段|概念入门:从认知到代码 理解 AI Agent 的工作原理与架构。推荐课程:Microsoft《AI Agents for Beginners》、Hugging Face《AI Agents》。核心学习点:感知、决策、行动、反馈循环机制。第二阶段|核心技术&…...
《奇迹 MU:荣耀出征》荣耀 12 区:职业选择 + 开荒路线 + 搬砖技巧全攻略!
作为正版奇迹 MU 授权的复古魔幻手游,《奇迹 MU:荣耀出征》的核心魅力不仅在于经典职业的热血回归与自由交易的搬砖乐趣,更在于从新手开荒到高阶攻坚的完整成长链路、全阶段高爆地图的刷宝惊喜、世界 BOSS 的全服混战与战盟攻城的巅峰对决。相…...
从手忙脚乱到从容不迫:DouyinLiveRecorder如何用智能代理池解决多平台直播录制难题
从手忙脚乱到从容不迫:DouyinLiveRecorder如何用智能代理池解决多平台直播录制难题 【免费下载链接】DouyinLiveRecorder 项目地址: https://gitcode.com/gh_mirrors/do/DouyinLiveRecorder 你是否曾经为了录制不同平台的直播内容而疲于奔命?当抖…...
零基础玩转Qwen2.5-7B:5分钟本地部署,小白也能跑通AI对话
零基础玩转Qwen2.5-7B:5分钟本地部署,小白也能跑通AI对话 1. 前言:为什么选择Qwen2.5-7B AI大模型正在改变我们与技术互动的方式,但对于普通用户来说,部署和使用这些模型往往充满挑战。Qwen2.5-7B作为阿里开源的最新…...
ECG-Emotion Recognition(情绪识别)实战指南:WESAD与DREAMER数据集深度解析与应用
1. 情绪识别与ECG技术入门指南 第一次接触ECG情绪识别时,我和大多数人一样充满疑惑:心跳数据真能反映人的情绪?经过三个月的项目实践,我可以肯定地说,ECG信号就像情绪的"心电图",愤怒时心跳加速、…...
终极指南:5个实用技巧解决Rainmeter开发中的内存保护异常问题
终极指南:5个实用技巧解决Rainmeter开发中的内存保护异常问题 【免费下载链接】rainmeter Desktop customization tool for Windows 项目地址: https://gitcode.com/gh_mirrors/ra/rainmeter 在Rainmeter桌面定制工具的开发过程中,内存保护异常&a…...
告别重复劳动:用快马AI自动生成akshare数据清洗与分析流水线
告别重复劳动:用快马AI自动生成akshare数据清洗与分析流水线 金融数据分析中,数据获取和清洗往往是最耗时的环节。每次研究新标的,我们都要重复编写类似的代码:从不同接口获取数据、对齐时间轴、处理缺失值、计算技术指标……这些…...
提升开放平台开发效率,快马AI工具链自动化集成与测试
在企业级开放平台的开发过程中,效率往往是决定项目成败的关键因素之一。传统的开发流程中,开发者需要花费大量时间在重复性工作上,比如编写API客户端代码、配置测试环境、维护文档等。这些工作不仅耗时,还容易出错。今天我想分享一…...
