【030】深入理解C++中的友元机制(friend)与最佳实践

本文详细介绍了C++中的友元机制,包括友元函数和友元类的概念、语法和应用实例。友元允许非成员函数或类访问类的私有成员,打破了数据隐藏原则,因此使用时需谨慎。文章通过示例展示了如何声明和使用友元,以及友元的一些注意事项,如不可继承、单向关系等。
摘要由CSDN通过智能技术生成

引言


💡 作者简介:专注分享高性能服务器后台开发技术知识,涵盖多个领域,包括C/C++、Linux、网络协议、设计模式、中间件、云原生、数据库、分布式架构等。目标是通过理论与代码实践的结合,让世界上看似难以掌握的技术变得易于理解与掌握。
💡 公众号:Lion 莱恩呀

👉
🎖️ CSDN实力新星、专家博主,阿里云博客专家、华为云云享专家
👉
🔔 专栏介绍:从零到c++精通的学习之路。内容包括C++基础编程、中级编程、高级编程;掌握各个知识点。
👉
🔔 专栏地址:C++从零开始到精通
👉
🔔 博客主页:https://blog.csdn.net/Long_xu


🔔 上一篇:【029】C++静态成员和 this 指针详解
🔔 下一篇:【031】C++类和对象之运算符重载详解和代码实践(最全讲解)

一、友元概述

C++中,友元(friend)是一种机制,它允许一个类的非成员函数或另一个类访问该类的私有成员。友元可以在类定义中声明,在类定义外部实现。

类的主要特点之一是数据隐藏,即类的私有成员无法在类的外部作用域之外访问。但是有时候需要在类的外部访问类的私有成员,因此出现了友元函数;友元函数是一种特权函数,允许访问私有成员。可以把一个全局函数、或者类的成员函数、甚至整个类声明为友元。

需要注意的是,友元机制破坏了类的封装性,因此应该谨慎使用。如果过度使用友元机制,会导致代码难以维护和扩展。

二、友元的语法

使用friend关键字声明为友元。声明一个友元函数或友元类的语法如下:

(1)友元函数的声明语法。

class ClassName {
private:
    // 私有成员
public:
    friend ReturnType FunctionName(ParameterList); // 声明友元函数
};

其中,ClassName为当前类名,ReturnType为友元函数的返回类型,FunctionName为友元函数名,ParameterList为参数列表。

(2)友元类的声明语法。

class ClassName1 {
private:
    // 私有成员
public:
    friend class ClassName2; // 声明友元类
};

其中,ClassName1为当前类名,ClassName2为被声明为友元的类名。

需要注意的是,在程序中定义和实现了一个被声明为该类的友元的全局函数或其他类中的成员函数时,在该函数前需要加上 friend ClassName::FunctionName() 来指定这个函数是属于哪个类。例如:

class MyClass {
private:
    int privateMember;

public:
    friend void friendFunction(MyClass obj);
};

void friendFunction(MyClass obj) {
    cout << "The value of private member is: " << obj.privateMember;
}

int main() {
    MyClass myObj;
    
    myObj.privateMember = 10;
    
    friendFunction(myObj); // 调用友元函数
    
    return 0;
}

如果将 friendFunction() 函数定义放在 MyClass 类外面,则需要使用以下语法:

void friend MyClass::friendFunction(MyClass obj) { ... }

三、友元的应用举例

3.1、普通全局函数作为类的友元

#include <iostream>
#include <string>
using namespace std;
class Room {
	friend int visiting(Room &room);
private:
	string bedRoom;//私有
public:
	string setingRoom;//公共
public:

	Room(string bedRoom, string setingRoom)
	{
		this->bedRoom = bedRoom;
		this->setingRoom = setingRoom;
	}
};

// 普通全局函数
int visiting(Room &room)
{
	cout << room.setingRoom << endl;
	cout << room.bedRoom << endl;//如果没有设置友元则无法访问
	return 0;
}

int main()
{
	Room room("Bed", "seting");
	visiting(room);
	return 0;
}

3.2、类的某个成员函数作为另一个类的友元

#include <iostream>
#include <string>
using namespace std;

class Room;// 向前声明,只能说明类名称

class Good {
public:
	int visiting01(Room &room);
	int visiting02(Room &room);
};
class Room {
	friend int Good::visiting02(Room &room);
private:
	string bedRoom;//私有
public:
	string setingRoom;//公共
public:

	Room(string bedRoom, string setingRoom)
	{
		this->bedRoom = bedRoom;
		this->setingRoom = setingRoom;
	}
};

// 成员函数
int Good::visiting01(Room &room)
{
	cout << room.setingRoom << endl;
	//cout << room.bedRoom << endl;// error
	return 0;
}

int Good::visiting02(Room &room)
{
	cout << room.setingRoom << endl;
	cout << room.bedRoom << endl;//如果没有设置友元则无法访问
	return 0;
}

int main()
{
	Room room("Bed", "seting");
	Good good;
	good.visiting01(room);
	good.visiting02(room);
	return 0;
}

3.3、整个类作为另一个类的友元

#include <iostream>
#include <string>
using namespace std;

class Room;// 向前声明,只能说明类名称
class Good {
public:
	int visiting01(Room &room);
	int visiting02(Room &room);
};
class Room {
	friend class Good;
private:
	string bedRoom;//私有
public:
	string setingRoom;//公共
public:

	Room(string bedRoom, string setingRoom)
	{
		this->bedRoom = bedRoom;
		this->setingRoom = setingRoom;
	}
};

// 成员函数
int Good::visiting01(Room &room)
{
	cout << room.setingRoom << endl;
	cout << room.bedRoom << endl;// OK
	return 0;
}

int Good::visiting02(Room &room)
{
	cout << room.setingRoom << endl;
	cout << room.bedRoom << endl;//如果没有设置友元则无法访问
	return 0;
}

int main()
{
	Room room("Bed", "seting");
	Good good;
	good.visiting01(room);
	good.visiting02(room);
	return 0;
}

四、友元的注意事项

  1. 友元函数可以访问类的私有成员和保护成员,因此需要谨慎使用。过度使用友元函数可能会破坏类的封装性,导致代码难以维护。

  2. 友元关系不能被继承。即使是派生类也不能访问基类中声明为友元的函数或类。

  3. 友元关系是单向的。如果A是B的友元,那么B并不一定是A的友元。

  4. 在定义友元函数时,需要在函数名前加上关键字“friend”,同时在类定义中声明该函数。

  5. 友元关系不具备传递性。即如果A是B的友元,B是C的友元,并不意味着A也是C的友元。

  6. 可以将一个类声明为另一个类的友元,这样就可以让该类中所有成员都能够访问另一个类中的私有成员和保护成员。

  7. 如果一个函数需要访问多个类中的私有成员和保护成员,可以将其声明为这些类中任意一个类的友元即可。

五、友元案例

设计一个电视机类,电视机属性有:

  • 开机和关机;
  • 音量;
  • 频道;
  • 操作音量的方法;
  • 操作频道的方法。

电视机只能逐一调整频道,不能指定频道。添加遥控类,遥控类除了拥有电视机 已有功能,再添加根据输入调台功能。

将遥控器类作为电视机类的友元类。

class TV;

class Remote {
private:
	TV *p;
public:
	Remote(TV *p)
	{
		this->p = p;
	}
	void onOrOff();
	void upVolume();
	void downVolume();
	void upChannel();
	void downChannel();
	void showTV();
	void setChannel(int num);
};


class TV {
	friend class Remote;
	enum{OFF,ON};
	enum{minVol,maxVol=100};
	enum{minChannel,maxChannel=28};
private:
	int status;
	int volume;
	int channel;
public:
	TV()
	{
		status = OFF;
		volume = minVol;
		channel = minChannel;
	}
	void onOrOff();
	void upVolume();
	void downVolume();
	void upChannel();
	void downChannel();
	void showTV();
};

void TV::onOrOff()
{
	status = (status == OFF ? ON : OFF);
}

void TV::upVolume()
{
	if (volume >= maxVol)
	{
		cout << "音量已经最大啦" << endl;
		return;
	}
	volume++;
}
void TV::downVolume()
{
	if (volume < minVol)
	{
		cout << "音量已经最小啦" << endl;
		return;
	}
	volume--;
}

void TV::upChannel()
{
	if (channel >= maxChannel)
	{
		cout << "频道已经最大啦" << endl;
		return;
	}
	channel++;
}
void TV::downChannel()
{
	if (channel < minChannel)
	{
		cout << "频道已经最小啦" << endl;
		return;
	}
	channel--;
}

void TV::showTV()
{
	cout << "电视机状态:" << (status == OFF ? "关" : "开") << endl;
	cout << "电视频道:" << channel << endl;
	cout << "电视音量:" << volume << endl;
	cout << "------------------------------------" << endl;
}

// -----------------------------------------
void Remote::onOrOff()
{
	p->onOrOff();
}

void Remote::upVolume()
{
	p->upVolume();
}
void Remote::downVolume()
{
	p->downVolume();
}

void Remote::upChannel()
{
	p->upChannel();
}
void Remote::downChannel()
{
	p->downVolume();
}

void Remote::showTV()
{
	p->showTV();
}

void Remote::setChannel(int num)
{
	if (num >= TV::minChannel && num<=TV::maxChannel)
	{
		p->channel = num;
		return;
	}
	cout << "输入的频道不合法" << endl;
}


int main()
{
	TV tv;
	tv.showTV();
	
	tv.onOrOff();
	tv.upChannel();
	tv.upChannel();
	tv.upVolume();
	tv.showTV();
	tv.onOrOff();

	cout << "使用遥控器:" << endl;
	Remote rtv(&tv);
	rtv.showTV();

	rtv.onOrOff();
	rtv.upChannel();
	rtv.upChannel();
	rtv.downChannel();
	rtv.upVolume();
	rtv.showTV();
	rtv.onOrOff();

	return 0;
}

输出:

电视机状态:关
电视频道:0
电视音量:0
------------------------------------
电视机状态:开
电视频道:2
电视音量:1
------------------------------------
使用遥控器:
电视机状态:关
电视频道:2
电视音量:1
------------------------------------
电视机状态:开
电视频道:4
电视音量:1
------------------------------------

六、总结

  1. 友元函数。友元函数是一个普通的非成员函数,但它被声明为某个类的友元。这意味着该函数可以访问该类的所有私有成员和保护成员。声明方式为在类定义中使用“friend”关键字,例如:

    class MyClass {
      friend void myFunction();
    };
    
  2. 友元类。友元类是指某个类A将另一个类B声明为自己的友元,在这种情况下,B可以访问A的私有成员和保护成员。声明方式为在类定义中使用“friend”关键字并加上要作为友元的类名,例如:

    class MyClass {
      friend class MyFriendClass;
    };
    
  3. 友元成员函数。友元成员函数是指某个函数被声明为另一个类A的友元,并且该函数所属于另一个与A无关的类B。这意味着该函数可以访问A对象中所有私有成员和保护成员。声明方式为在B类定义中使用“friend”关键字和A类型参数,例如:

    class MyClassA {
      friend void MyClassB::myFunction(MyClassA&);
    };
    
  4. 友元作用域。友元的作用域是在声明它的类中,而不是在被声明为友元的函数或类中。因此,在同一作用域中可以使用相同名称的友元,但它们分别属于不同的类。

  5. 友元与继承。子类不能直接访问父类的私有成员,但如果将子类声明为父类的友元,则子类就可以访问父类的私有成员和保护成员。这种情况下,应该谨慎考虑是否破坏了封装性。

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Lion Long

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

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

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

打赏作者

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

抵扣说明:

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

余额充值