C语言日记 37 类的友元(1)(全局函数做友元)

news2024/12/25 13:06:13

根据36 类和对象-友元-全局函数做友元_哔哩哔哩_bilibili复现出如下代码:

一、(只是)访问(公开)内部成员:

#include <iostream>
using namespace std;
class Building
{
private:
	int ws;//卧室
public:
	int kt;//客厅
	Building();
};
Building::Building()//构造函数赋初值
	{
		int ws = 1;
		int kt = 2;
	}
int visit(Building b)
{

	cout << "客厅=" << b.kt << endl;
	return 0;
}
int main()
{
	Building b1;
	visit(b1);
}

在这里,实际上,我们也可以把构造函数写在类里面,实现同样的效果:

class Building
{
private:
	int ws;//卧室
public:
	int kt;//客厅
	Building()//构造函数赋初值
	{
		int ws = 1;
		int kt = 2;
	}
};

 结果:

为什么我写了半天,最后返回的是一个随机值呢?

根本原因,就在于构造函数的格式不对,构造函数在赋初值时,类里面的变量(成员)前不应该(不能)有类型说明

(C语言日记 33 构造函数_宇 -Yu的博客-CSDN博客中,就有构造函数定义和声明的使用格式)

如果没有类型说明,那么构造函数相当于还在给类的(公有,私有)成员变量赋初值,

而加上了类型说明,那么相当于构造函数在给函数内自定义的变量赋初值,和类里面的成员没有关系,而类的成员实际上并没有被赋值,输出时返回的自然就是一个随机值;所以,我们应将程序改为:(通过实参形参调用传值)

#include <iostream>
using namespace std;
class Building
{
private:
	int ws;//卧室
public:
	int kt;//客厅
	Building();
};
Building::Building()//构造函数赋初值
{
	 ws = 1;
	 kt = 2;
}
int visit(Building b)
{

	cout << "客厅=" << b.kt << endl;
	return 0;
}
int main()
{
	Building b1;
	visit(b1);
}

结果:

修改为36 类和对象-友元-全局函数做友元_哔哩哔哩_bilibili源程序所设计的各个模块的样子:

#include <iostream>
using namespace std;
class Building
{
private:
	int ws;//卧室
public:
	int kt;//客厅
	Building()//构造函数赋初值
	{
		ws = 1;
		kt = 2;
	}
};
//
int visit(Building b)
{
	cout << "客厅=" << b.kt << endl;
	return 0;
}
void test()
{
	Building b1;
	visit(b1);
}
int main()
{
	test();
}

我们可以通过实参形参调用传值,也可以用指针,引用的方式传值:

指针:

int visit(Building* b)
{
	cout << "客厅=" << b->kt << endl;
	return 0;
}
void test()
{
	Building b1;
	visit(&b1);
}

注解:

(1):这里的“->”,表示:通过指针来访问结构体变量(的具体内容)C语言日记 28 结构体类型_宇 -Yu的博客-CSDN博客

(2):

visit(&b1);

中,&表示取(地)址,不能改为*(如果改为*,则表示访问该指针指向的目标对象的具体内容)

如果一定不想这样写的话,可以改写为:

void test()
{
	Building b;
	Building *b1=&b;
	visit(b1);
}

引用:

int visit(Building &b)
{
	cout << "客厅=" <<b.kt << endl;
	return 0;
}
void test()
{
	Building b;
	visit(b);
}

注解:

(1):引用访问,就和对象访问自身的内容属性引用,不能用“->”;(“->”只能用于指针)

(2):

	visit(b);

只能这么写,不能在前面加“*”(加了表求指向目标的值),也不能加“&”(加了表求指向目标的地址)

二、访问内部私有成员(这就要用到友元了)

这时,既然我们想要让全局函数访问内部私有成员,只要在前面的基础上

在visit()函数中写一个输出私有函数的语句:

    cout << "卧室=" << b.ws << endl;

并且在类的成员声明里面加(上)一个友元函数的声明:

    friend void visit(Building b);

值得注意的是:

这里的友元函数的声明加在类(体)的任意处都可以:

友元函数不是成员函数,无论是放在private私有成员说明中:

#include <iostream>
using namespace std;
class Building
{
private:
	int ws;//卧室
	friend void visit(Building b);
public:
	int kt;//客厅
	Building()//构造函数赋初值
	{
		ws = 1;
		kt = 2;
	}
};
//
void visit(Building b)
{
	cout << "客厅=" << b.kt << endl;
	cout << "卧室=" << b.ws << endl;
}
void test()
{
	Building b1;
	visit(b1);
}
int main()
{
	test();
}

还是放在成员说明之外:

#include <iostream>
using namespace std;
class Building
{
	friend void visit(Building b);
private:
	int ws;//卧室
public:
	int kt;//客厅
	Building()//构造函数赋初值
	{
		ws = 1;
		kt = 2;
	}
};
//
void visit(Building b)
{
	cout << "客厅=" << b.kt << endl;
	cout << "卧室=" << b.ws << endl;
}
void test()
{
	Building b1;
	visit(b1);
}
int main()
{
	test();
}

最终结果都可以访问类的内部私有成员:

 

例8-14 使用普通函数计算两点间的距离。

源程序:

#include <iostream>
#include <cmath>
using namespace std;
class Point	//Point 类定义
{
private://私有数据成员
	int x, y;
public://外部接口
	Point(int x = 0, int y = 0) :x(x), y(y) { }
	int getX() { return x; }
	int getY() { return y; }
	friend float dist(Point& a, Point& b); //友元函数是一个普通函数
};
float dist(Point& a, Point& b)//普通函数的定义
{
	double x = a.x - b.x;
	double y = a.y - b.y;
	return(float)sqrt(x * x + y * y);
}
//
int main()
{
	Point p1(3, 5), p2(11, 6);
	cout << "The distance is:";
	cout << dist(p1, p2) << endl;
	//调用普通友元函数
	return 0;
}

结果:

The distance is:8.06226

(1):#include <cmath>:相当于#include <math.h> (C语言)<C++的库>,当然这里不写也无所谓,如同:C语言日记 8 C++语句_宇 -Yu的博客-CSDN博客:#include <math.h>//可有可无

(2):其中的: friend float dist(Point &a, Point &b); 只是一个用引用作为函数参数的形式,可以改(写)为:(实参形参传递数据)

friend float dist(Point a, Point b);  

详见书P105(当然,像这样用实参形参传递数据,采用的是值传递的传值的方式);

也可以改(写)为:(指针传递数据)

#include <iostream>
#include <cmath>
using namespace std;
class Point	//Point 类定义
{
private://私有数据成员
	int x, y;
public://外部接口
	Point(int x = 0, int y = 0) :x(x), y(y) { }
	int getX() { return x; }
	int getY() { return y; }
	friend float dist(Point* a, Point* b); //友元函数是一个普通函数
};
float dist(Point* a, Point* b)//普通函数的定义
{
	double x = a->x - b->x;
	double y = a->y - b->y;
	return(float)sqrt(x * x + y * y);
}
//
int main()
{
	Point p1(3, 5), p2(11, 6);
	cout << "The distance is:";
	cout << dist(&p1, &p2) << endl;
	//调用普通友元函数
	return 0;
}

 当然,改写为指针传递数据的方式的修改过程,比起其他两种情况要更为复杂一些:

改动处:

(1):形参a和b改为指针

(2):“->”改为"."

(3):调用dist函数时,里面的实参p1,p2改为取(地)址“&p1,&p2”

(3):另外,(如果)我们想要在类中(里面)定义该友元函数(不用分开声明,直接一次性定义完成就行了)最终结果也一样:

#include <iostream>
using namespace std;
class Point	//Point 类定义
{
private://私有数据成员
	int x, y;
public://外部接口
	Point(int x = 0, int y = 0) :x(x), y(y) { }
	int getX() { return x; }
	int getY() { return y; }
	friend float dist(Point a, Point b)//友元函数是一个普通函数
	{
		double x = a.x - b.x;
		double y = a.y - b.y;
		return(float)sqrt(x * x + y * y);
	}
};
//
int main()
{
	Point p1(3, 5), p2(11, 6);
	cout << "The distance is:";
	cout << dist(p1, p2) << endl;
	//调用普通友元函数
	return 0;
}

而在这里我们可以看到,实际上想要访问本类的内部私有成员,根本还用不着什么友元函数,

直接设立一个公有的类函数直接访问调用私有成员就可以:

#include <iostream>
#include <cmath>
using namespace std;
class Point	//Point 类定义
{
private://私有数据成员
	int x, y;
public://外部接口
	Point(int x = 0, int y = 0) :x(x), y(y) { }
	int getX() { return x; }
	int getY() { return y; }
	float dist(Point* a, Point* b)//普通函数的定义
	{
		double x = a->x - b->x;
		double y = a->y - b->y;
		return(float)sqrt(x * x + y * y);
	}
};
//
int main()
{
	Point p1(3, 5), p2(11, 6);
	cout << "The distance is:";
	cout << dist(&p1, &p2) << endl;
	//调用普通友元函数
	return 0;
}

结果:

为什么???

调用类中定义的函数必须用对象(才能调用):(这里用p1,p2都可以)

#include <iostream>
#include <cmath>
using namespace std;
class Point	//Point 类定义
{
private://私有数据成员
	int x, y;
public://外部接口
	Point(int x = 0, int y = 0) :x(x), y(y) { }
	int getX() { return x; }
	int getY() { return y; }
	float dist(Point* a, Point* b)//普通函数的定义
	{
		double x = a->x - b->x;
		double y = a->y - b->y;
		return(float)sqrt(x * x + y * y);
	}
};
//
int main()
{
	Point p1(3, 5), p2(11, 6);
	cout << "The distance is:";
	cout << p1.dist(&p1, &p2) << endl;
	//调用普通友元函数
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/25729.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NewStarCTF 公开赛-web

week1 HTTP cookie 修改admin 源码发现key GET和POST传参即可 Head?Header! User-Agent: CTF Referer: ctf.com X-Forwarded-For: 127.0.0.1我真的会谢 信息泄露 robots.txt www.zip 源码 /.index.php.swp NotPHP 函数绕过 if(file_get_contents($_GET[data]) &qu…

数据备份一般有哪些方式,如何进行数据备份

在如今这个时代&#xff0c;数据于我们而言是特别重要的&#xff0c;一旦发生丢失&#xff0c;可能会带来严重后果&#xff0c;如果你也很苦恼如何保证这些重要数据的安全&#xff0c;小编建议一定要对重要数据进行备份&#xff0c;以防发生丢失的意外。数据备份一般有哪些方式…

设计一个缓存策略,动态缓存热点数据

&#x1f468;‍&#x1f4bb;个人主页&#xff1a; 才疏学浅的木子 &#x1f647;‍♂️ 本人也在学习阶段如若发现问题&#xff0c;请告知非常感谢 &#x1f647;‍♂️ &#x1f4d2; 本文来自专栏&#xff1a; 常见场景解决方案 &#x1f308; 每日一语&#xff1a;努力不一…

C语言 0 —— 信息在计算机中的表示

计算机的电路 由 逻辑门电路组成。一个逻辑门电路可以看成为一个开关&#xff0c;每个开关的状态是“开” 则 高电位 对应 1 或者 “关” 则 低电位 对应 0 &#xff0c; 那么1和0 刚刚好用二进制数来表示&#xff1a; 每个位只能取1和0 &#xff0c;称为 one 个 bit &#…

谈一谈关于Linux内核编译详解原理

前言&#xff1a;为什么要做这个启动盘&#xff0c;因为内核编译是很危险的&#xff0c;中间出了错系统则直接崩溃&#xff0c;然后就无法开机了&#xff0c;你将看到一个_在你的左上角闪烁。知道启动盘可以帮你从外置设备启动系统&#xff0c;能启动系统才能恢复系统。1.编译前…

Java 线程和反射---尚硅谷Java视频学习

1.Java程序在运行得时候默认就会产生一个进程2.这个进程会有一个主线程3.代码都在主线程中执行 线程的生命周期 线程的执行方式 public class Java02_Thread {public static void main(String[] args) throws Exception {// TODO 线程 - 执行方式&#xff08;串行&#xff0c…

ASP.NET Core 3.1系列(14)——分布式缓存Redis的使用

1、前言 前一篇博客介绍了ASP.NET Core中本地缓存MemoryCache的使用方法。相较于本地缓存&#xff0c;分布式缓存更加适合大多数项目应用场景&#xff0c;下面就来介绍一下如何在ASP.NET Core中对Redis缓存进行相关操作。 2、分布式缓存接口——IDistributedCache 对于分布式…

pytorch深度学习实战24

第二十四课 VGG网络 VGG是Oxford的Visual Geometry Group的组提出的&#xff08;大家应该能看出VGG名字的由来了&#xff09;。该网络是在ILSVRC 2014上的相关工作&#xff0c;主要工作是证明了增加网络的深度能够在一定程度上影响网络最终的性能。VGG有两种结构&#xff0c;分…

远程重启电脑

一、设置电脑允许自动启动 在远程计算机上编辑设置远程注册表 步骤1. 按“WindowsR”键调用运行对话框。输入“services.msc”并点击“确定”。 步骤2. 在“服务”窗口中&#xff0c;双击“RemoteRegistry”以检查其状态。 步骤3. 将启动类型更改为“自动”。 二、查找远程计…

SPARKSQL3.0-各阶段自定义扩展规则源码剖析

一、前言 这一节主要介绍如何自定义扩展各阶段规则 虽然spark内部提供了很多优化规则&#xff0c;但在实际工作中&#xff0c;经常因为业务需求需要自定义扩展优化器或解析器&#xff0c;故自己实现一个优化器才对sparksql有更深的理解 二、扩展范围 spark在扩展方便做的很…

vue.js毕业设计,基于vue.js前后端分离教室预约小程序系统设计与实现

功能介绍 【后台管理功能模块】 系统设置&#xff1a;设置关于我们、联系我们、加入我们、法律声明 广告管理&#xff1a;设置小程序首页轮播图广告和链接 留言列表&#xff1a;所有用户留言信息列表&#xff0c;支持删除 会员列表&#xff1a;查看所有注册会员信息&#xff0…

从零开始学前端:DOM、BOM、焦点事件 --- 今天你学习了吗?(JS:Day20)

从零开始学前端&#xff1a;程序猿小白也可以完全掌握&#xff01;—今天你学习了吗&#xff1f;&#xff08;JS&#xff09; 复习&#xff1a;从零开始学前端&#xff1a;CSSOM视图模式 — 今天你学习了吗&#xff1f;&#xff08;JS&#xff1a;Day19&#xff09; 文章目录从…

java8 (jdk 1.8) 新特性——Stream ApI

在java8 中&#xff0c;有两个最重要的改变&#xff0c;一个就是之前了解的Lmbda java8 (jdk 1.8) 新特性——Lambda ,还有一个就是Stream Api 1. 什么是Stream API 简单来说就是一个类库&#xff0c;里边有一些方法方便我们对集合数据进行操作&#xff0c;就好像使用 SQL 语…

Windows cmd 命令及Linux 环境下导入导入mysql 数据库

文章目录一、背景二、Windows cmd 导入导出mysql 数据库1.导出数据库三种方式&#xff08;导出数据库时不需要连接数据库&#xff09;2. 操作步骤2.导入数据库三、linux 环境下导入导出数据库一、背景 最近在本机上安装了一个WMware 虚拟机&#xff0c;需要从本机&#xff08;…

从三层架构说起,谈谈对历史项目的小改造

项目背景说明 最近接手一个 “老” 项目的需求修改&#xff0c;项目整体基于 .net core 3.1 平台&#xff0c;以传统的三层架构为基础构建。了解需求后&#xff0c;逐步对原有项目框架进行大概的了解&#xff0c;主要是熟悉一些框架的开发规范&#xff0c;基本工具类库的使用&…

寒亭5.8万亩盐碱稻 国稻种芯·中国水稻节:山东潍坊插秧期

寒亭5.8万亩盐碱稻 国稻种芯中国水稻节&#xff1a;山东潍坊插秧期 新京报讯&#xff08;记者赵利新&#xff09;新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯中国水稻节 中国三农智库网-功能性农业农业大健康大会报道&#xff…

MMRotate 全面升级,新增 BoxType 设计

引言&#xff1a;大大降低水平框检测器改旋转框检测器的难度 MMRotate 是一个基于 PyTorch 和 MMDetection 的开源旋转框目标检测工具箱。它将目标检测从水平框扩展到旋转框&#xff0c;为场景文字、遥感影像、自动驾驶等领域的应用打下了基础&#xff0c;为学术界和产业界提供…

瞄准镜-第12届蓝桥杯Scratch选拔赛真题精选

[导读]&#xff1a;超平老师计划推出Scratch蓝桥杯真题解析100讲&#xff0c;这是超平老师解读Scratch蓝桥真题系列的第82讲。 蓝桥杯选拔赛每一届都要举行4~5次&#xff0c;和省赛、国赛相比&#xff0c;题目要简单不少&#xff0c;再加上篇幅有限&#xff0c;因此我精挑细选…

数据结构——单链表

一.简介 上一篇文章&#xff0c;我们介绍了线性表中的顺序表。 而顺序表拥有一些缺陷 1.空间不够时需要增容&#xff0c;增容需要付出代价 2.为避免重复扩容&#xff0c;我们进行指数扩容&#xff0c;可能会造成空间浪费 3.顺序表从开始位置连续存储&#xff0c;插入删除数…

卡特尔世界杯来了,只喝精酿啤酒不玩望京扑克,其实也是一种缺失

北京时间2022年11月20日&#xff0c;卡特尔世界杯正式拉开了序幕&#xff0c;全球都进入了世界杯时间。世界杯的开幕&#xff0c;最高兴的还是球迷朋友&#xff0c;大家可以欢聚一堂&#xff0c;喝着精酿啤酒看着足球&#xff0c;那滋味别提多舒服了。 世界杯对于广大球迷来说&…