【C++ | 友元(friend)】友元函数、友元类、友元成员函数详解及例子代码

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 🍭
⏰发布时间⏰:

本文未经允许,不得转发!!!



在这里插入图片描述

🎄一、概述

一般来说,访问私有类成员的唯一方法是使用类方法。C++使用友元(friend)来避开这种限制。

C++的友元是为了解决这样的问题:有时需要类外部的函数来访问私有成员。这个问题在学习C++运算符时就会遇到。

本文主要介绍C++的三种友元实现,以及了解怎样编写自己的友元:

  • 友元函数;
  • 友元类;
  • 友元成员函数。

在这里插入图片描述

🎄二、友元函数

友元函数:一般是在类内声明为友元(friend)的全局函数。声明后,该函数可以访问类的私有成员。

为什么需要友元函数?
在实现类的二元运算符时,大部分情况可以将运算符函数写成类的成员函数,然后让类的对象作为左操作数去调用该运算符函数,如:CB = CA + 1;。但是,如果需要实现等式CB = 1 + CA,就无法调用该函数,而且这种形式也没法用成员函数去实现。这时就需要在类外部实现该函数,而且该函数还需要访问类的私有成员,而这样的函数只有该类的友元函数。关于这段描述不清楚的可以看下面举例的代码。

怎样声明、定义友元函数?
我们以CDate类为例:
1、将友元函数的原型放在类声明中,并在原型声明前加上关键字 friend
2、编写友元函数定义,因为它不是类的成员函数,所以不需要加类名作用域。

class CDate
{
	friend CDate operator+(int day, const CDate &date);	// 友元函数声明
	...
};
CDate operator+(int day, const CDate &date)	// 友元函数定义
{
	CDate temp = date;
	temp.m_day += day;
	cout << "Calling operator+(int, CDate)" << ", temp=" << &temp << endl;
	return temp;
}

友元函数例子完整代码

// g++ 18_friend_fun.cpp 
#include <iostream>
using namespace std;

class CDate
{
	friend CDate operator+(int day, const CDate &date);	// 友元函数声明
public:
	CDate(int year, int mon, int day);	// 构造函数声明
	CDate(const CDate& date);			// 拷贝构造函数声明
	CDate operator+(int day);			// 加号运算符声明

private:
	int m_year;
	int m_mon;
	int m_day;
};

// 构造函数定义
CDate::CDate(int year, int mon, int day)
{
	m_year = year;
	m_mon = mon;
	m_day = day;
	cout << "Calling Constructor" << ", this=" << this <<endl;
}

// 拷贝构造函数定义
CDate::CDate(const CDate& date)
{
	m_year = date.m_year;
	m_mon = date.m_mon;
	m_day = date.m_day;
	cout << "Calling Copy Constructor" << ", this=" << this << ", Copy Data" <<endl;
}

// 加号运算符定义
CDate CDate::operator+(int day)
{
	CDate temp = *this;
	temp.m_day += day;
	cout << "Calling operator+(int)" << ", this=" << &temp << endl;
	return temp;
}

// 友元函数定义
CDate operator+(int day, const CDate &date)
{
	CDate temp = date;
	temp.m_day += day;
	cout << "Calling operator+(int, CDate)" << ", temp=" << &temp << endl;
	return temp;
}

int main()
{
	CDate date(2024,6,17);
	CDate CB = CA + 1;
	CB = 1 + CA;	// 如果没有实现友元函数,则这句报错
	return 0;
}

运行结果如下,可以看到分别调用了operator+(int)operator+(int, CDate) 函数。
在这里插入图片描述


在这里插入图片描述

🎄三、友元类

友元类:一般是在类内声明为友元(friend)的类。声明后,友元类的所有成员函数函数都可以访问类的私有成员。

什么时候需要定义友元类?
假如程序要定义一个 空调类(CAirCond) 和一个 遥控器类(CRemote),这两个类存在一定的关系,但空调和遥控器显然不是继承的关系。而遥控器又可以改变空调的状态,也就是说 遥控器类 可以访问 空调类 的私有成员。这时就需要将 遥控器类 声明为 空调类 的友元类。

怎样声明、定义友元类?
1、在类中使用关键字 friend声明友元类;
2、编写友元类声明和定义;

class CAirCond	// 空调
{
	friend class CRemote; // 声明友元类
	...
};
class CRemote	// 遥控器
{
	...
};

友元类例子完整代码

// g++ 18_friend_class.cpp 
#include <iostream>
using namespace std;

class CAirCond	// 空调
{
	friend class CRemote; // 声明友元类
public:
	enum{OFF, ON};
	CAirCond(){state=OFF; temperature=26;}
	void setTemperature(int temp){temperature = temp;}
	void show()
	{
		cout << "air: state=" << (state==ON?"ON":"OFF") << ", temperature=" << temperature << endl;
	}
private:
	int state;	// 开关状态
	int temperature;// 温度
};

class CRemote	// 遥控器
{
public:
	CRemote(int mode=0){m_mode=mode;}
	void AirCondOn(CAirCond &air){air.state = CAirCond::ON;}
	void AirCondOff(CAirCond &air){air.state = CAirCond::OFF;}
	void setTemperature(CAirCond &air, int temp){air.setTemperature(temp);}
private:
	int m_mode;	// 0-制冷、1-制热
};

int main()
{
	CAirCond airConditioner;
	airConditioner.show();
	cout << endl;
	
	CRemote remote;
	remote.AirCondOn(airConditioner);
	airConditioner.show();
	cout << endl;
	
	remote.setTemperature(airConditioner,23);
	airConditioner.show();
	cout << endl;
	
	remote.AirCondOff(airConditioner);
	airConditioner.show();
	cout << endl;
	return 0;
}

运行结果如下:
在这里插入图片描述


在这里插入图片描述

🎄四、友元成员函数

友元成员函数:一般是在类内声明为友元(friend)的其他类的成员函数。声明后,友元成员函数可以访问类的私有成员。

什么时候需要定义友元成员函数?
如果某个类只有一两个成员函数需要访问本类的私有成员,可以只是将这一两个成员函数声明为本类的友元成员函数,而不用声明整个类为友元。例如,上个小节的 CRemote类 只有两个成员函数会访问 CAirCond类 的私有成员,可以只是声明这两个成员函数为 CRemote类 的友元。待会会给出这样操作的例子代码。

怎样声明、定义友元成员函数?
友元成员函数的声明、定义会有些复杂,下面以上个小节的 CAirCond 类、CRemote类 为例,分三步说明:

  • 1、在 CAirCond类 中使用关键字 friend声明友元成员函数,需要加上类名作用域CRemote::
    class CAirCond	// 空调
    {
    	friend void CRemote::AirCondOn(CAirCond &air); // 声明友元成员函数
    	friend void CRemote::AirCondOff(CAirCond &air);
    	...
    };
    
  • 2、将友元成员函数所属类(CRemote)的完整声明写在本类(CAirCond)的前面,因为使用了友元成员函数所属类的成员,所以需要其完整声明前置,否则会报错。
    并且 CRemote类 完整声明里不能使用 CAirCond 的成员,否则又需要将 CAirCond 类的完整声明放到 CRemote 类前面,会造成无解的循环,所以只能将上个小节在 CRemote 类声明的一些内联函数移动到类外去实现;
    class CRemote	// 遥控器
    {
    public:
    	CRemote(int mode=0){m_mode=mode;}
    	void AirCondOn(CAirCond &air);
    	void AirCondOff(CAirCond &air);
    	void setTemperature(CAirCond &air, int temp);
    private:
    	int m_mode;	// 0-制冷、1-制热
    };
    class CAirCond	// 空调
    {
    	friend void CRemote::AirCondOn(CAirCond &air); // 声明友元成员函数
    	friend void CRemote::AirCondOff(CAirCond &air);
    	...
    };
    
  • 3、将 CAirCond 类声明放在友元成员函数所属类的前面,因为所属类 CRemote 用到了 CAirCond 引用的参数:
    class CAirCond;
    class CRemote	// 遥控器
    {
    public:
    	CRemote(int mode=0){m_mode=mode;}
    	void AirCondOn(CAirCond &air);
    	void AirCondOff(CAirCond &air);
    	void setTemperature(CAirCond &air, int temp);
    private:
    	int m_mode;	// 0-制冷、1-制热
    };
    class CAirCond	// 空调
    {
    	friend void CRemote::AirCondOn(CAirCond &air); // 声明友元成员函数
    	friend void CRemote::AirCondOff(CAirCond &air);
    	...
    };
    

友元类例子完整代码

// g++ 18_friend_member_fun.cpp 
#include <iostream>
using namespace std;

class CAirCond;

class CRemote	// 遥控器
{
public:
	CRemote(int mode=0){m_mode=mode;}
	void AirCondOn(CAirCond &air);
	void AirCondOff(CAirCond &air);
	void setTemperature(CAirCond &air, int temp);
private:
	int m_mode;	// 0-制冷、1-制热
};

class CAirCond	// 空调
{
	friend void CRemote::AirCondOn(CAirCond &air); // 声明友元成员函数
	friend void CRemote::AirCondOff(CAirCond &air);
public:
	enum{OFF, ON};
	CAirCond(){state=OFF; temperature=26;}
	void setTemperature(int temp){temperature = temp;}
	void show()
	{
		cout << "air: state=" << (state==ON?"ON":"OFF") << ", temperature=" << temperature << endl;
	}
private:
	int state;	// 开关状态
	int temperature;// 温度
};

inline void CRemote::AirCondOn(CAirCond &air){air.state = CAirCond::ON;}
inline void CRemote::AirCondOff(CAirCond &air){air.state = CAirCond::OFF;}
inline void CRemote::setTemperature(CAirCond &air, int temp){air.setTemperature(temp);}

int main()
{
	CAirCond airConditioner;
	airConditioner.show();
	cout << endl;
	
	CRemote remote;
	remote.AirCondOn(airConditioner);
	airConditioner.show();
	cout << endl;
	
	remote.setTemperature(airConditioner,23);
	airConditioner.show();
	cout << endl;
	
	remote.AirCondOff(airConditioner);
	airConditioner.show();
	cout << endl;
	return 0;
}

运行结果如下:
在这里插入图片描述


在这里插入图片描述

🎄五、友元的其他关系

✨5.1 让两个类互为友元

有时候 类A 需要访问 类B 的私有成员,而 类B 也需要访问 类A 的私有成员,这时可以让这两个类互相成为对方的友元类。我们修改友元那个例子的代码如下,让 CAirCond类、CRemote类 互为友元:

// g++ 18_friend_class_each_other.cpp 
#include <iostream>
using namespace std;
class CRemote;
class CAirCond	// 空调
{
	friend class CRemote; // 声明友元类
public:
	enum{OFF, ON};
	CAirCond(){state=OFF; temperature=26;}
	void setTemperature(int temp){temperature = temp;}
	void show()
	{
		cout << "air: state=" << (state==ON?"ON":"OFF") << ", temperature=" << temperature << endl;
	}
	void setRemoteMode(CRemote &remote, int mode);
private:
	int state;	// 开关状态
	int temperature;// 温度
};

class CRemote	// 遥控器
{
	friend class CAirCond;
public:
	CRemote(int mode=0){m_mode=mode;}
	void AirCondOn(CAirCond &air){air.state = CAirCond::ON;}
	void AirCondOff(CAirCond &air){air.state = CAirCond::OFF;}
	void setTemperature(CAirCond &air, int temp){air.setTemperature(temp);}
private:
	int m_mode;	// 0-制冷、1-制热
};

void CAirCond::setRemoteMode(CRemote &remote, int mode){remote.m_mode=mode;}

int main()
{
	CAirCond airConditioner;
	airConditioner.show();
	cout << endl;
	
	CRemote remote;
	remote.AirCondOn(airConditioner);
	airConditioner.show();
	cout << endl;
	
	remote.setTemperature(airConditioner,23);
	airConditioner.show();
	cout << endl;
	
	remote.AirCondOff(airConditioner);
	airConditioner.show();
	cout << endl;
	return 0;
}

✨5.2 共同友元

需要使用友元的另一种情况是,函数需要访问两个类的私有数据。从逻辑上看,这样的函数应是每个类的成员函数,但这是不可能的。它可以是一个类的成员,同时是另一个类的友元,但有时将函数作为两个类的友元更合理。下面使用伪代码举例:

class A{
	friend void ChangeAB(CA &a, CB &b);
	...
}
class B{
	friend void ChangeAB(CA &a, CB &b);
	...
}
void ChangeAB(CA &a, CB &b)
{
	...
}

在这里插入图片描述

🎄六、总结

本文介绍了C++的友元函数、友元类、友元成员函数、其他友元关系,以及使用例子介绍了如何声明、定义、使用。

关于C++的友元又几个注意点:

  • 1、友元的声明仅仅指定了访问的权限, 而非一个通常意义上的函数声明。
  • 2、友元声明只能出现在类定义的内部,但是在类内出现的具休位置不限。一般,最好在类定义开始或结束前的位置集中声明友元。
  • 3、如果类中使用到其他类的成员,则需要将被使用的类的完整声明前置。
  • 4、友元不能被继承。
    在这里插入图片描述
    如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁
  • 57
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 23
    评论
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wkd_007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值