草庐IT

C++:友元(看这一篇就够了)

孙 悟 空 2023-04-05 原文

文章目录

生活中你的家有客厅(public),有你的卧室(private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去

但是呢,你也可以允许 隔壁老王 进去。

在程序里,有些私有属性 也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术

友元的目的 就是让一个函数或者类 访问另一个类中的私有成员

友元的关键字为 friend

友元的三种实现

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元

1 全局函数做友元

首先,我们要定义一个房屋类,公共成员变量为客厅,私有成员变量为卧室

class Building
{
	// Building的构造函数,给成员变量赋初值
	Building()
	{
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}
	
	string m_SittingRoom;	// 客厅

private:

	string m_BedRoom;		// 卧室
};

然后定义一个全局函数 laoWang(),用来访问Building类中的私有成员

void laoWang1(Building *building)
{
	cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_SittingRoom << endl;

	cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_BedRoom << endl;
}

当然也可以用引用传递或者最简单的值传递

void laoWang2(Building &building)
{
	cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_SittingRoom << endl;

	cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_BedRoom << endl;
}

void laoWang3(Building building)
{
	cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_SittingRoom << endl;

	cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_BedRoom << endl;
}

最后定义一个测试函数test(),实现 laoWang() 这个全局函数做友元访问类的私有成员

void test()
{
	Building building;
	laoWang1(&building);
	laoWang2(building);
	laoWang3(building);
}

但是,现在还不能实现全局函数访问类的私有成员!

关键代码

friend void laoWang1(Building *building);
friend void laoWang2(Building &building);
friend void laoWang3(Building building);

在Building类中声明友元函数,告诉编译器 laoWang全局函数是 Building类 的好朋友,可以访问Building对象的私有成员

class Building
{
	// 告诉编译器 laoWang全局函数是 Building类  的好朋友,可以访问Building对象的私有成员
	friend void laoWang1(Building *building);
	friend void laoWang2(Building &building);
	friend void laoWang3(Building building);

public:

	Building()
	{
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}
	
	string m_SittingRoom;	// 客厅

private:

	string m_BedRoom;		// 卧室
};

下面给出全局函数做友元访问类的私有成员的完整示例代码

#include <iostream>
#include <string>

using namespace std;

// 房屋类
class Building
{
	// 告诉编译器 laoWang全局函数是 Building类  的好朋友,可以访问Building对象的私有成员
	friend void laoWang1(Building *building);
	friend void laoWang2(Building &building);
	friend void laoWang3(Building building);

public:

	Building()
	{
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}
	
	string m_SittingRoom;	// 客厅

private:

	string m_BedRoom;		// 卧室
};



//全局函数
void laoWang1(Building *building)
{
	cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_SittingRoom << endl;

	cout << "隔壁老王 全局函数 正在访问:(地址传递) " << building->m_BedRoom << endl;
}

void laoWang2(Building &building)
{
	cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_SittingRoom << endl;

	cout << "隔壁老王 全局函数 正在访问:(引用传递) " << building.m_BedRoom << endl;
}

void laoWang3(Building building)
{
	cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_SittingRoom << endl;

	cout << "隔壁老王 全局函数 正在访问:( 值传递 ) " << building.m_BedRoom << endl;
}

void test()
{
	Building building;
	laoWang1(&building);
	laoWang2(building);
	laoWang3(building);
}


int main()
{
	test();
}

输出结果

隔壁老王 全局函数 正在访问:(地址传递) 客厅
隔壁老王 全局函数 正在访问:(地址传递) 卧室
隔壁老王 全局函数 正在访问:(引用传递) 客厅
隔壁老王 全局函数 正在访问:(引用传递) 卧室
隔壁老王 全局函数 正在访问:( 值传递 ) 客厅
隔壁老王 全局函数 正在访问:( 值传递 ) 卧室

2 类做友元

首先,声明一个要访问的私有变量所属的Building类,防止在下面的好LaoWang类中,编译器不认识Building(当然也可以采取先定义Building类,再定义隔壁老王LaoWang类,这样就不用声明Building类了)

class Building;

然后定义一个隔壁老王LaoWang类,声明了一个Building类型的指针变量building。其中,成员函数采用另一种方式:类内声明,类外定义,可以简化类的内容(在全局函数做友元的示例中,采用在类内声明并定义成员函数的方式,两种方式均可)

class LaoWang
{
public:
	//LaoWang的构造函数
	LaoWang();

	void visit();	//参观函数  访问Building中的属性

	Building * building;


private:


};

接着给出Building类的定义

class Building
{
public:
	//Building的构造函数类内声明,在类外定义
	Building();
		
	string m_SittingRoom;	// 客厅
	
private:
	
	string m_BedRoom;		// 卧室

};

下面给出类外定义成员函数,需要注意的是,在类外定义,需要在成员函数加上所在类的作用域(类名::成员函数名()),以便于告诉编译器,该成员函数属于哪个类。

//Building类的构造函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";

	cout << m_SittingRoom << endl;
	cout << m_BedRoom << endl;

}

//LaoWang的构造函数
LaoWang::LaoWang()
{
	// 创建建筑物对象
	building = new Building;	//在堆区开辟一块内存,并赋给building指针

}

//LaoWang的成员函数visit()
void LaoWang::visit()
{
	cout << "隔壁老王类正在访问:" << building->m_SittingRoom << endl;

	cout << "隔壁老王类正在访问:" << building->m_BedRoom << endl;
}

最后定义一个测试函数,实现类做友元访问其他类的私有成员

void test()
{
	LaoWang lw	//定义一个LaoWang类的对象;
	lw.visit();
}

但是,现在还不能实现一个类访问另一个类的私有成员!

关键代码

friend class LaoWang;

在Building类中声明一个友元类,告诉编译器 LaoWang类是 Building类 的好朋友,可以访问Building对象的私有成员

class Building
{
	// 告诉编译器,LaoWang类是Building类的好朋友,可以访问Building类的私有成员
	friend class LaoWang;
public:
	
	Building();
		
	string m_SittingRoom;	// 客厅
	
private:
	
	string m_BedRoom;		// 卧室

};

下面给出类做友元实现一个类访问另一个类私有成员的完整示例代码

#include <iostream>
#include <string>

using namespace std;

// 类作友元

class Building;
class LaoWang
{
public:

	LaoWang();

	void visit();	//参观函数  访问Building中的属性

	Building * building;


private:


};

// 房屋类
class Building
{
	// 告诉编译器,LaoWang类是Building类的好朋友,可以访问Building类的私有成员
	friend class LaoWang;
public:
	
	Building();
		
	string m_SittingRoom;	// 客厅
	
private:
	
	string m_BedRoom;		// 卧室
};

// 类外定义成员函数

Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}

LaoWang::LaoWang()
{
	// 创建建筑物对象
	building = new Building;
}

void LaoWang::visit()
{
	cout << "隔壁老王LaoWang类正在访问:" << building->m_SittingRoom << endl;

	cout << "隔壁老王LaoWang类正在访问:" << building->m_BedRoom << endl;
}

void test()
{
	LaoWang lw;
	lw.visit();
}

int main()
{
	test();

	return 0;
}

输出结果

隔壁老王LaoWang类正在访问:客厅
隔壁老王LaoWang类正在访问:卧室

3 成员函数做友元

类似于类作友元,我们首先声明一个Building类,防止在下面的好LaoWang类中,编译器不认识Building

class Building;

然后定义LaoWang类,同样采用成员函数在类内声明,类外定义的方式。其中定义两个访问函数

  • visit1(),可以 访问Building中的私有成员
  • visit2(),不可以 访问Building中的私有成员
class LaoWang
{
public:

	LaoWang();
	void visit1();	//让visit1()函数   可以 访问Building中的私有成员
	void visit2();	//让visit2()函数 不可以 访问Building中的私有成员

	Building *building;

private:

};

下面给出Building类的定义

class Building
{
public:
	Building();

	string m_SittingRoom;	//客厅
	
private:

	string m_BedRoom;		//卧室
};

下面给出类外定义成员函数

//LaoWang类的构造函数
LaoWang::LaoWang()
{
	building = new Building;
}

//LaoWang类的成员函数visit1()
void LaoWang::visit1()
{
	cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_SittingRoom << endl;
	cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_BedRoom << endl;	
}

//LaoWang类的成员函数visit2()
void LaoWang::visit2()
{
	cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_SittingRoom << endl;
	//cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_BedRoom << endl;	//错误!私有属性不可访问
}

//Building类的构造函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}

最后用一个测试函数实现成员函数做友元实现对另一个类私有成员的访问

void test()
{
	LaoWang lw;
	
	lw.visit1();

	lw.visit2();
}

同样的,现在还没有声明友元,因此类中的成员函数还不能访问另一个类的私有成员

关键代码

friend void LaoWang::visit1();

在Building类中声明一个友元成员函数,告诉编译器 **visit1()**成员函数是 Building类 的好朋友,可以访问Building对象的私有成员

class Building
{
	// 告诉编译器,LaoWang类下的visit1()函数是Building类的好朋友,可以访问Building的私有成员
	friend void LaoWang::visit1();

public:
	Building();

	string m_SittingRoom;	//客厅
	
private:

	string m_BedRoom;		//卧室
};

下面给出成员函数做友元实现一个类的成员函数访问另一个类私有成员的完整示例代码

#include <iostream>
#include <string>

using namespace std;

class Building;

class LaoWang
{
public:

	LaoWang();
	void visit1();	//让visit1()函数   可以 访问Building中的私有成员
	void visit2();	//让visit2()函数 不可以 访问Building中的私有成员

	Building *building;

private:

	
};

class Building
{
	// 告诉编译器,LaoWang类下的visit1()函数是Building类的好朋友,可以访问Building的私有成员
	friend void LaoWang::visit1();

public:
	Building();

	string m_SittingRoom;	//客厅
private:

	string m_BedRoom;		//卧室
};


LaoWang::LaoWang()
{
	building = new Building;
}

void LaoWang::visit1()
{
	cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_SittingRoom << endl;
	cout << "隔壁老王LaoWang类中的visit1()函数正在访问:" << building->m_BedRoom << endl;
}

void LaoWang::visit2()
{
	cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_SittingRoom << endl;
	//cout << "隔壁老王LaoWang类中的visit2()函数正在访问:" << building->m_BedRoom << endl;	//错误!私有属性不可访问
}

Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}

void test()
{
	LaoWang lw;
	
	lw.visit1();

	lw.visit2();
}

int main()
{
	test();
	
	return 0;
}

输出结果

隔壁老王LaoWang类中的visit1()函数正在访问:客厅
隔壁老王LaoWang类中的visit1()函数正在访问:卧室
隔壁老王LaoWang类中的visit2()函数正在访问:客厅

参考连接黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难

有关C++:友元(看这一篇就够了)的更多相关文章

  1. 【保姆级】python最新版3.11.1开发环境搭建,看这一篇就够了 - 2

    【保姆级】Python最新版开发环境搭建,看这一篇就够了(适用于Python3.11.2安装)文章目录【保姆级】Python最新版开发环境搭建,看这一篇就够了(适用于Python3.11.2安装)一、Python解释器安装Windows安装步骤环境变量配置(非必要)MacOS安装步骤Linux安装步骤二、PyCharm安装三、创建Python工程工欲善其事必先利其器,在使用Python开发程序之前,在计算机上搭建Python开发环境是必不可少的环节,目前Python最新稳定版本是3.11.1,且支持到2027年,如下图所示本文手把手带你从0到1搭建Python最新版3.11.1开发环境,堪称保

  2. 接口测试重点内容看这一篇就够了 - 2

    1、接口的概念系统与系统之间,组件与组件之间,数据传递交互的通道2、接口的类型按协议划分:http、tcp、IP按语言划分:C++、java、PHP……按范围划分:系统之间多个内部系统之间内部系统与外部系统之间程序之间方法与方法之间、函数与函数之间、模块与模块之间3、接口测试的概念对系统或组件之间的接口进行测试,校验传递的数据正确性和逻辑依赖关系的正确行。4、接口测试的原理主要针对服务器,模拟客户端向服务器发送请求,通过工具或者代码来测试服务器针对客户端请求回发的响应数据是否与预期结果一致。5、接口测试的特点符合质量控制前移的理念可以发现一些页面操作发现不了的问题接口测试低成本高效益接口测试是

  3. 零基础学Linux运维,看这一篇就够了(含30G自学教程笔记) - 2

    作为一个10年老运维,在开始这篇文章之前,先送给大家一句话:干啥不好,非要做运维,听人劝,吃饱饭,趁年轻,换行吧!好了,不开玩笑了,回到正文中来。当谈到运维职业发展情况时,很多人都会说运维做不长久,然后劝人做两年就赶快转研发吧!总之是全面唱衰运维!但作为一个老运维,我想说的是:运维转开发确实是一个不错的选择,但运维做不长久则完全是对运维的偏见了!很多人有运维做不长久的偏见的原因其实和运维职业的特性有关,运维有三个老生常谈的特点:打杂,背锅,睡的少!说运维打杂,是说运维工作比较宽泛,运维职业门槛不高,什么都得会一点。公司里但凡跟计算机有关的事,可能都会找到运维,这就导致了运维工作比较杂!至于背黑

  4. 必看新手教程!一篇就够!pycharm链接云服务器--yolov5 yolov7训练自己的数据集(矩池云) - 2

    趁着寒假期间稍微尝试跑了一下yolov5和yolov7的代码,由于自己用的笔记本没有独显,台式机虽有独显但用起来并不顺利,所以选择了租云服务器的方式,选择的平台是矩池云(价格合理,操作便捷)需要特别指出的是,如果需要用pycharm链接云服务器训练,必须要使用pycharm的专业版而不是社区版,专业版可以使用SSH服务连接云服务器。关于专业版的获取,据我所知一是可以买,二是如果你是在校大学生,可以用学生证向JetBrain申请专业版使用权,我就是通过这种方式激活专业版账户的,我记得当时两三天官方就发激活邮件了,还是很人性化的,使用期一年。下面开始正题本教程只涉及将yolov5及yolov7跑通

  5. javascript - 对于 JavaScript 多维数组的深拷贝,深入一层似乎就足够了。这是真的吗? - 2

    注意:我只是一个编码新手,所以这个问题的核心可能存在明显的错误或误解。本质上,我需要在JavaScript中“按值”深度复制多维数组到未知深度。我原以为这需要一些复杂的递归,但似乎在JavaScript中您只需要深复制一个级别就可以按值复制整个数组。举个例子,这是我的测试代码,使用了一个故意复杂的数组。functiontest(){vararr=[['ok1'],[],[[],[],[[],[[['ok2'],[]]]]]];varcloned=cloneArray(arr);arr='';//Deletetheoriginalalert(cloned);}functioncloneA

  6. Linux - 一篇带你读懂 Curl Proxy 代理模式 - 2

    curl是一个很有名的处理网络请求的类Unix工具。出于某种原因,我们进行网络请求,需要设置代理。本文讲全面介绍如何为curl设置代理设置代理参数基本用法-x,--proxy[protocol://]host[:port]设置HTTP代理下面两种设置代理的方式是可以的curl-x"http://user:pwd@127.0.0.1:1234""http://httpbin.org/ip"curl--proxy"http://user:pwd@127.0.0.1:1234""http://httpbin.org/ip"由于代理地址的默认协议为 HTTP,所以可以省略,按照下面的形式也是可以的cu

  7. 一篇学完:王道考研408数据结构(全) - 2

    笔记首发于:lengyueling.cnPDF版本附在 lengyueling.cn 对应文章结尾,欢迎下载访问交流绪论数据结构在学什么如何用程序代码把现实世界的问题信息化如何用计算机高效地处理这些信息从而创造价值数据结构的基本概念什么是数据:数据是信息的载体,是描述客观事物属性的数、字符及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。数据是计算机程序加工的原料。现代计算机处理的数据:现代计算机——经常处理非数值型问题对于非数值型的问题:我们关心每个个体的具体信息我们还关心个体之间的关系数据元素:数据元素是数据的基本单位,通常作为一个整体进行考虑和处理。数据项:一个数据元素可由若干

  8. javascript - 如何重构这一堆 if 语句? - 2

    假设我有一个名为incomingValue的变量,并且我从API获取一个数字作为它的值。这些值介于0到1之间,我正在根据此值使用一堆if语句设置另外两个变量,如下所示。varincomingValue;//SetbyanAPIvarsetValueName;varsetValueIcon;if(incomingValue0.09&&incomingValue在实际实现中,我有大约10个if语句检查特定时间间隔,直到1。例如如果它大于0.10但小于0.16等,则执行此操作。作为一个JavaScript初学者,我觉得这不是正确的做事方式,尽管它可以完成工作。我将如何重构这段代码?更新:根据

  9. javascript - 是否有必要使用 === 来比较 Javascript 中的字符串或者 == 就足够了? - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:JavaScript===vs==:Doesitmatterwhich“equal”operatorIuse?我能理解为什么===在比较数字、bool值、空字符串等时由于意外的类型转换是必需的,例如varfoo=1;varbar=true;//bar==foo=>true//bar===foo=>false但是,在将变量与非空字符串文字进行比较时,==是否会引入错误?在这种情况下,使用==是否比===更有效?

  10. 元宇宙日,定在这一天,你可能没料到 - 2

    【序】双11民间戏称光棍节购物狂欢节,迄今猫狗13岁也是中国空军72岁生日也是腾讯23岁生日她还是“元宇宙日”11月11日,中国移动通信联合会元宇宙产业委员会(下称“元宇宙产业委”)揭牌仪式举办。中国移动通信联合会元宇宙产业委员会创始发起人、《元宇宙》作者于佳宁被任命为元宇宙产业委执行主任委员。为促进元宇宙产业和产业元宇宙健康快速发展,物链芯工程技术研究院、元宇宙实验室联合多家企业、科研院校、行业专家,共同组建“元宇宙共识圈”并发起成立中国移动通信联合会元宇宙产业委员会。元宇宙产业委宣布将11月11日设立为“元宇宙日”并发布《元宇宙产业宣言》(下称《宣言》)。《宣言》提出,元宇宙是第三代互联网

随机推荐