友元、隐式类型转化

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不宜多用。
友元分为:友元函数和友元类

(一)友元函数

友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。

友元函数不是类的成员函数,就相当于你的朋友再亲密也不是你的家人,既然不是类成员函数,那和普通成员函数调用一样,不需要通过对象调用

特征:

  1. 友元函数可访问类的私有和保护成员,但不是类的成员函数
  2. 友元函数不能用const修饰
  3. 友元函数可以在类定义的任何地方声明,不受类访问限定符限制
  4. 一个函数可以是多个类的友元函数
  5. 友元函数的调用与普通函数的调用原理相同

 (二)友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。

特征:

  1. 友元关系是单向的,不具有交换性。比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
  2. 友元关系不能传递,如果C是B的友元, B是A的友元,则不能说明C时A的友元。
  3. 友元关系不能继承

(三)内部类

概念:如果一个类定义在另一个类的内部,这个内部类就叫做内部类。内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越的访问权限。
注意:内部类就是外部类的友元类,参见友元类的定义,内部类可以通过外部类的对象参数来访问外部类中的所有成员。但是外部类不是内部类的友元。


特性:


1. 内部类可以定义在外部类的public、protected、private都是可以的。

2. 注意内部类可以直接访问外部类中的static成员,不需要外部类的对象/类名。

3. sizeof(外部类)=外部类,和内部类没有任何关系。

(四)隐式类型转化

class A
{
public:
	A(int a)
		:_a(a)
	{}

private:
	int _a; 
};
int main()
{
	A a1(1);

	A a2 = 3;
	return 0;
}

内置类型对象隐式转换成自定义类型对象,能支持这个转换,是因为有A的需要传一个参数(int)构造函数支持。

如果没有合适的构造函数,则不能进行转换:

class A
{
public:
	A(int a)
		:_a(a)
	{}

private:
	int _a; 
};
int main()
{
	A a2 = 3;
	int* p = nullptr;
	A a3 = p;
	return 0;
}

如果A类具有int*作为参数的构造函数,就可以进行转化:

class A
{
public:
	A(int a)
		:_a(a)
	{}
	A(int* a)
	{}
private:
	int _a;
};
int main()
{
	A a2 = 3;
	int* p = nullptr;
	A a3 = p;
	return 0;
}

并且用于转化产生的临时变量具有常性:

class A
{
public:
	A(int a)
		:_a(a)
	{}

private:
	int _a; 
};
int main()
{
	A a1(1);

	A a2 = 3;
	A& a3 = 3; // 临时变量具有常性
	return 0;
}

3->const A temp(3)-> A& a3 = const A temp,发生错误。

总结:如果你想要进行隐式类型转化,就需要有合适的构造函数。

如果你不想进行隐式类型转化,可以使用explicit关键字。

explicit

explicit修饰构造函数,禁止类型转换

class A
{
public:
	explicit A(int a)
		:_a(a)
	{}
private:
	int _a; 
};
int main()
{
	A a2 = 3;
	return 0;
}

注意:虽然不能进行隐式类型转化,但如果我们显式地进行转化还是可以进行转化的。

class A
{
public:
	explicit A(int a)
		:_a(a)
	{}
private:
	int _a; 
};
int main()
{
	A a2 = (A)3; // 显式转化
	return 0;
}

隐式类型转化的应用

class A
{
public:
	A(int a)
		:_a(a)
	{}
private:
	int _a; 
};

class Date
{
public:
	Date(int year = 2024, int month = 1, int day = 16)
		:_year(year), _month(month), _day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	// 单参数类型转化
	list<A>lt;
	A aa1(1);
	lt.push_back(aa1); // 第一种

	lt.push_back(A(2)); // 第二种

	lt.push_back(3); // 第三种

	// 多参数类型转化
	list<Date>lt1;
	Date d6(2023, 11, 2);
	lt1.push_back(d6); // 第一种

	lt1.push_back(Date(2023, 11, 2)); // 第二种

	lt1.push_back({ 2023,11,2 }); // 第三种
	return 0;
}

(五)编译器对拷贝构造和构造函数的优化

注意:这个部分取决于编译器,编译器不同操作会有所不同。

同一个表达式中,构造+构造 / 构造+拷贝构造 / 拷贝构造+拷贝构造会被编译器优化成为调用一个函数:

  1. 构造+构造->构造
  2. 构造+拷贝构造->构造
  3. 拷贝构造+拷贝构造->拷贝构造

下面使用这个类型举一些例子:

class A
{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	A(const A& c)
		:_a(c._a)
	{
		cout << "A(A&)" << endl;
	}
private:
	int _a; 
};

  • 构造+拷贝构造->构造

int main()
{
	A aa1 = 1;
	return 0;
}

原先的步骤是:用1构造一个临时对象,再用临时对象拷贝构造aa1。这样就要调用一个构造函数和一个拷贝构造函数两个函数。

但由于编译器的优化,只需要调用一个构造函数即可:

void func(A aa)
{}
int main()
{
	A aa(1); // 构造函数
	func(aa); // 拷贝构造函数
	return 0;
}

这个程序虽然也是先调用构造函数构造出aa对象,再调用拷贝函数进行传参调用func函数,但是编译器却没有“合二为一”。这是因为这两个步骤不在同一个表达式中。

例如,这样就可以“合二为一”:

int main()
{
	func(A(2));
	return 0;
}

或者这样:

int main()
{
	func(3);
	return 0;
}
  • 拷贝构造+拷贝构造->拷贝构造

A func()
{
	A aa; // 构造函数
	return aa; // 拷贝构造
}

int main()
{
	A ret = func(); // 拷贝构造
	return 0;
}

原先需要调用拷贝构造将aa拷贝给一个临时变量,再将这个临时变量用拷贝构造函数构造ret,经过编译器的优化,只调用一个拷贝构造函数:

注意:一个表达式中,连续拷贝构造+赋值重载->无法优化

aa2 = func3(); 和 A aa1 = func3(); 是不一样的:前者是一个赋值行为,后者是一个拷贝构造行为。

  • 构造+拷贝构造->优化为一个构造

void func(A aa) // 传参-拷贝构造
{
	
}

int main()
{
	func(A(2)); // 构造
	return 0;
}

今天的分享就到这里了,如果,你感觉这篇博客对你有帮助的话,就点个赞吧!感谢感谢……

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

L1-058 6翻了(Java)

“666”是一种网络用语&#xff0c;大概是表示某人很厉害、我们很佩服的意思。最近又衍生出另一个数字“9”&#xff0c;意思是“6翻了”&#xff0c;实在太厉害的意思。如果你以为这就是厉害的最高境界&#xff0c;那就错啦 —— 目前的最高境界是数字“27”&#xff0c;因为这…

JVM系列-1.初识JVM

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring原理、JUC原理、Kafka原理、分布式技术原理、数据库技术、JVM原理&#x1f525;如果感觉博主的文…

【QT+QGIS跨平台编译】之二:【zlib+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、zlib介绍二、文件下载三、文件分析四、pro文件五、编译实践 一、zlib介绍 zlib是一套通用的解压缩开源库&#xff0c;提供了内存&#xff08;in-memory&#xff09;压缩和解压函数。zlib是一套通用的解压缩开源库&#xff0c;提供了内存&#xff08;in-memory&am…

JavaScript DOM表单相关操作之表单相关事件

1、焦点事件 焦点事件就是鼠标的光标事件&#xff0c;点到输入框中&#xff0c;叫做获得焦点事件&#xff0c;当鼠标离开这个输入框时叫做失去焦点事件。 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>知数SEO_专注搜…

计算机网络安全——密码学入门

网络安全是指在网络领域、专业领域的网络安全包括在基础计算机网络基础设施中所做的规定&#xff0c;网络管理员采取的策略来保护网络及网络可访问资源免受未经授权的访问&#xff0c;以及对其有效性&#xff08;或缺乏&#xff09;的持续不断的监控和测量的结合。 1. 密码学的…

什么是网络安全?网络安全概况

网络安全涉及保护我们的计算机网络、设备和数据免受未经授权的访问或破坏。 这个领域包括多种技术、过程和控制措施&#xff0c;旨在保护网络、设备和数据免受攻击、损害或未授权访问。网络安全涉及多个方面&#xff0c;包括但不限于信息安全、应用程序安全、操作系统安全等 …

K8S搭建(centos)二、服务器设置

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

“gradle project sync failed”

很久没打开AndroidStudio了&#xff0c;打开电脑发现这个软件都没了。重新安装后创建项目不成功&#xff0c;就提示了这个错误。 错误原因 “gradle project sync failed”&#xff1a;gradle没有配置成功。在安卓项目下找到目标文件&#xff1a;gradle --> wrapper -->…

HarmonyOS 发送http网络请求

好 本文 我们来说 http请求 首先 我们要操作网络内容 需要申请权限 项目中找到 main目录下的module.json5 最下面加上 "requestPermissions": [{"name": "ohos.permission.INTERNET"} ]这里 我在本地写了一个get接口 大家可以想办法 弄一个后…

【JavaEE进阶】 关于⽇志框架(SLF4J)

文章目录 &#x1f333;SLF4j&#x1f332;⻔⾯模式(外观模式)&#x1f6a9;⻔⾯模式的定义&#x1f6a9;⻔⾯模式的优点 &#x1f343;关于SLF4J框架&#x1f6a9;不引⼊⽇志⻔⾯&#x1f6a9;引⼊⽇志⻔⾯ ⭕总结 &#x1f333;SLF4j SLF4J不同于其他⽇志框架,它不是⼀个真正…

基于 GPT 和 Qdrant DB 向量数据库, 我构建了一个电影推荐系统

电影推荐系统自从机器学习时代开始以来就不断发展&#xff0c;逐步演进到当前的 transformers 和向量数据库的时代。 在本文中&#xff0c;我们将探讨如何在向量数据库中高效存储数千个视频文件&#xff0c;以构建最佳的推荐引擎。 在众多可用的向量数据库中&#xff0c;我们将…

【Linux】Shell 命令以及运行原理

Shell 命令以及运行原理 当用户登录 Linux 系统的时候&#xff0c;系统会给用户创建一个新的进程&#xff0c;一般叫做 bash&#xff08;命令行解释器&#xff09;。 Linux 严格意义上说的是一个操作系统&#xff0c;我们称之为 “核心&#xff08; kernel &#xff09;” &…

Paimon教程

教程来自尚硅谷 目录 教程来自尚硅谷1. 概述1.1 简介1.2 核心特性1.3 文件布局1.3.1 LSM Trees 2. 集成Flink2.1 安装&配置2.2 Catalog 3. 进阶使用3.1 写入性能3.1.1 并行度3.1.2 Compaction3.1.3 触发Compaction的Sorted Run数3.1.4 写入初始化3.1.5 内存 3.2 读取性能3.…

在Windows 11上安装Domino 14和Traveler 14

大家好&#xff0c;才是真的好。 是的&#xff0c;没错&#xff0c;在过去&#xff0c;很多人都喜欢将他们的Domino服务器安装在自己的pc电脑和个人操作系统上&#xff0c;从遥远的windows 2000、xp&#xff1b;windows 7以及到现在的Windows 10和11。 这也造成了在使用Domin…

【设计模式】代理模式的实现方式与使用场景

1. 概述 代理模式是一种结构型设计模式&#xff0c;它通过创建一个代理对象来控制对另一个对象的访问&#xff0c;代理对象在客户端和目标对象之间充当了中介的角色&#xff0c;客户端不再直接访问目标对象&#xff0c;而是通过代理对象间接访问目标对象。 那在中间加一层代理…

抓包工具Fidder

介绍 Fiddler是强大的抓包工具&#xff0c;它的原理是以web代理服务器的形式进行工作的&#xff0c;代理地址&#xff1a;127.0.0.1&#xff0c;默认端口号:8888。代理就是在客户端和服务器之间设置一道官咖&#xff0c;客户端将请求数据发送出去之后&#xff0c;代理服务器会…

为什么电脑降价了?

周末&#xff0c;非常意外地用不到3000元买到了一款2023年度发布的华为笔记本I5,16G,500G&#xff0c;基本是主流配置&#xff0c;我非常意外&#xff0c;看了又看&#xff0c;不是什么Hwawii&#xff0c;或者Huuawe。然后也不是二手。为什么呢&#xff1f;因为在ALU和FPU之外&…

一零七七、将Hexo cl Hexo g Hexo s通过systemctl命令管理

背景&#xff1a; 服务器需要执行hexo s来运行项目&#xff0c;但这个命令是基于前台的&#xff0c;故想直接嫁接在systemctl命令基础上来控制环境&#xff1a; Centos 8 前置环境就不说了,Hexo安装好&#xff0c;起码装完自己得先看hexo命令生效没&#xff0c;前置环境做好后…

项目篇:基于UDP通信模型的网络聊天室

思维导图 基于UDP通信模型的网络聊天室 消息分类及数据包结构 服务器端 #include <head.h> #define SER_PORT 8888 #define SER_IP "192.168.232.133" typedef struct mb {struct sockaddr_in cin;char name[20];struct mb *next; }*member; //群发消息 int …

【英文干货】【Word_Search】找单词游戏(第1天)

本期主题&#xff1a;Mindfulness&#xff08;意识力&#xff09; 本期单词&#xff1a; Awareness 意识 Breathing 呼吸 Calm 平静的 De-Stress 减压 Feelings 感受&#xff0c;情感 Inspection 调查 Meditation 冥想 Peace 和平 Quiet 安静的 Recollection 回忆 R…