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; //怪物id
string 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;
//dest
dest.m_blood -= skillVal; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的非共有属性
if (dest.m_blood < 0)
dest.m_blood = 0;
//src
src.m_blood += skillVal; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_blood
//因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_name
cout << 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; //怪物id
string m_name; //怪物名字
int m_blood; //血量
Skill m_skill; //技能
};
//施法
void Skill::spell(Monster &src, Monster &dest)
{
switch (m_skillType)
{
case BLOOD:
{
int blood = 0;
//dest
dest.m_blood -= m_val; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的非共有属性
if (dest.m_blood < 0)
dest.m_blood = 0;
//src
src.m_blood += m_val; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_blood
//因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_name
cout << 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; //怪物id
string m_name; //怪物名字
int m_blood; //血量
Skill m_skill; //技能
};
//施法
void Skill::spell(Monster &src, Monster &dest)
{
switch (m_skillType)
{
case BLOOD:
{
int blood = 0;
//dest
dest.m_blood -= m_val; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的非共有属性
if (dest.m_blood < 0)
dest.m_blood = 0;
//src
src.m_blood += m_val; //因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_blood
//因为Skill类是Monster类的友元,所以可以直接访问Monster类的私有成员变量m_name
cout << 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类的友元。
一般情况下,用友元函数重载<<,>>操作符