当前位置: 首页 > news >正文

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),但这会破坏另一个类的封装性。所以在实际的开发过程中,我们应该按照实际需求选择是否用友元。

友元的特性

  1. 单向性:比如上面的例子中,Skill类是Monster类的友元,但Monster类不是Skill类的友元

  1. 友元不能被继承:比如上面的例子中Skill类是Monster类的友元,假如SceneSkill类是Skill类的子类,SceneSkill类不是Monster类的友元。

  1. 一般情况下,用友元函数重载<<,>>操作符

http://www.lryc.cn/news/35994.html

相关文章:

  • [手写OS]动手实现一个OS 之X86实模式下的汇编开发
  • 【Linux内核二】常用的网络丢包错包debug工具介绍
  • qt控件增加渐变色效果
  • 【node : 无法将“node”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。 最全面有效的解决方案】
  • 打怪升级之字符串的分界符与字符串替换
  • 载荷台子使用方式
  • 1005 继续(3n + 1)猜想
  • VMware15配置NAT模式联通网络
  • doPost的实际使用
  • 2017年MathorCup数学建模A题流程工业的智能制造解题全过程文档及程序
  • HNU-电子测试平台与工具2-数模转换
  • CentOS7安装Telnet客户端和服务端和使用方式
  • 脂肪毒性的新兴调节剂——肠道微生物组
  • 【JavaSE系列】 第九节 —— 多态那些事儿
  • ego微商小程序项目-测试步骤
  • 华为OD机试用Python实现 -【报数游戏】2023Q1 A卷
  • Plsql使用
  • 小丑改造计划之四线程控制
  • Spring注册Bean的几种方式
  • Egg:使用joi进行参数校验以及注册接口小demo
  • 天梯赛训练L1-016(查验身份证)
  • 技术方案评审
  • Python机器学习库scikit-learn在Anaconda中的配置
  • yarn init 没有 ts 类型声明
  • 孩子喜欢打人父母要怎么引导?听听专家的小建议
  • Hive中order by,sort by,distribute by,Cluster by
  • PyTorch的自动微分(autograd)
  • sum-check protocol
  • 数据结构刷题(二十一):131分割回文串、78子集
  • Spring Aop 详解