C++:类和对象 III(初始化列表、explicit、友元、匿名对象)

news2024/9/21 13:53:08

目录

初始化列表

初始化列表的特点

类型转换、explicit

隐式类型转换

explicit关键字

static成员

静态成员变量

静态成员函数

友元

友元函数

友元类

内部类

匿名对象

编译器优化


初始化列表

初始化列表就是类成员初始化的地方

函数有它声明和定义的地方,变量也有,类成员也有

先来看看日期类的默认构造函数

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2024, 7, 15);

	return 0;
}

在类里面显示出来的成员我们把它叫做声明

如果你认为我们就这样把_year,_month,_day给初始化了,那就错了

这并不是初始化,这是赋值

如果我们要初始化类成员的话应该这样做

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};

 

这一块就是初始化列表

先是一个冒号开始,然后以逗号分隔,成员变量后面跟初始值或者表达式

这样我们就完成了一个成员的定义和初始化

那么这两种写法有什么区别呢?

这两种写法给我们带来的效果都是一样的,但是无论怎么写我们的成员变量都要经过初始化列表一遍,就算没有写初始化列表也会!所以这里建议尽量使用初始化列表初始化

如果我们不知道初始化列表怎么给,我们可以在声明的地方给一个缺省值,例如:

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
private:
	int _year = 1900;
	int _month = 1;
	int _day = 1;
};

这样就算我们没有写初始化列表也会给我们自动初始化上这些值

注意:

const成员变量,带引用的成员变量,只有一次初始化的机会!那就是在初始化列表中!

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

因为const常量不能被赋值,只能在初始化的地方被初始化,正确代码如下: 

class A
{
public:
	A(int a = 1)
		:_a(a)
	{}
private:
	const int _a;
};
class A
{
public:
	A(int a = 1)
		:_a(a)
	{}
private:
	int& _a;
};

初始化列表的特点

如果我们没有在声明的地方给缺省值,也没有写初始化列表,那么值由编译器决定

如果我们给了缺省值没有写初始化列表,那么会根据缺省值初始化

如果我们即给了缺省值也给了初始化列表,那么根据初始化列表的值初始化

注:上述行为于构造函数内部行为无关

初始化列表中按照成员在类中声明的定义来初始化,于初始化列表中出现的先后顺序无关

例如:

class A
{
public:
	A()
		:_a(1)
		, _b(_a)
	{}
//private:
	int _b;
	int _a;
};

int main()
{
	A a;
	cout << a._a << endl;
	cout << a._b << endl;
	return 0;
}

输出结果:

我们可以看到_a的值为1,_b的值为随机值s

这是因为类声明是先声明的_b,才声明的_a,所以初始化列表会先初始化_b,再初始化_a,而不是因为_a在初始化列表中初始化就初始化_a

类型转换、explicit

隐式类型转换

什么是隐式类型转换?通过下面的例子我想你就明白了

int main()
{
	double d = 3.14;
	int i = d;
	cout << i << endl;

	return 0;
}

double类型的d为什么能赋值给int类型的i?

在 i = d 的时候这里会构造出一个临时对象,d会先构造给这个临时变量,然后临时变量才会将值拷贝构造给i

回到正题

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

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

这里将1拷贝构造给A类型a就是隐式类型转换,将int类型转换成A类型

explicit关键字

如果我们想要编译器再严格一点,只能同类型转换,那么我们可以在构造类型前面加上关键字explicit

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

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

此时我们就运行不了我们的代码了

static成员

静态成员变量

用static修饰的成员变量,称为静态成员变量,静态成员变量必须要在类外初始化!

它的作用和C语言中一样,不属于某个具体的对象,存放在静态区中,生命周期跟全局变量一致

类内初始化:

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

    void Print()
	{
		cout << _a << endl;
	}
private:
	static int _a;
};

int main()
{
	A a;
    a.Print();
	return 0;
}

类外初始化:

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

	void Print()
	{
		cout << _a << endl;
	}
private:
	static int _a;
};

int A::_a = 5;

int main()
{
	A a;
	a.Print();
	return 0;
}

静态成员函数也收public、private、protected访问限定符约束

class A
{
public:
	//A(int a = 1)
	//	:_a(a)
	//{}

	void Print()
	{
		cout << _a << endl;
	}
private:
	static int _a;
};

int A::_a = 5;

int main()
{
	A a;
	cout << A::_a << endl;
	return 0;
}

静态成员函数

用static修饰的成员函数,称之为静态成员函数,静态成员函数没有this指针!

静态成员函数中可以访问静态成员,但是不能访问成员函数,因为没有this指针

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

	static void Print()
	{
		cout << _a << endl;
	}
private:
	int _a;
};

非静态成员函数可以访问任意的静态成员变量和静态成员函数(毕竟是全局的)

友元

友元分为友元函数友元类

友元函数

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

void Print(const A& a)
{
	cout << a._a << endl;
}

正常这样Print函数是无法访问到A类里面的private成员变量的,但是如果它是A类的朋友就可以了

class A
{
	friend void Print(const A& a);

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

void Print(const A& a)
{
	cout << a._a << endl;
}

友元函数仅仅是一种声明,他不是类的成员函数

把函数的第一行写一遍,然后前面加上friend,这样Print就是A的朋友了,这样就不会报错了

可以看出,外部友元函数是可以访问类中的私有成员的,当然保护和共有也不例外

友元类

class A
{
public:
	A(int a = 1)
		:_a(a)
	{}
	
	void Print()
	{
		B b;
		cout << b._b << endl;
	}
private:
	int _a;
};

class B
{
	friend class A;
public:
	B(int b = 0)
		:_b(b)
	{}
private:
	int _b;
};

正常来说A类中的Print函数是不能访问b中private的_b

但是这时候,B声明了A是我的朋友,那么A中就可以访问B类中的私有了

但是要注意:友元类关系不能传递

B说A是我的朋友,但是不代表A的朋友是B

class A
{
public:
	A(int a = 1)
		:_a(a)
	{}
	
	void Print()
	{
		B b;
		cout << b._b << endl;
	}
private:
	int _a;
};

class B
{
	friend class A;
public:
	B(int b = 0)
		:_b(b)
	{}

	void Print()
	{
		A a;
		cout << a._a << endl;
	}
private:
	int _b;
};

A是B的友元,但是B可不是A的友元! 

友元和面向对象封装的思想有些相反了,破坏了这种封装

友元会增加耦合度,但是破坏了封装,所以不宜多用

内部类

如果一个类定义在另一个类的内部,那么这个类叫做内部类

class A
{
public:
	A(int a = 1)
		:_a(a)
	{}
	class B
	{
	public:
		void func(const A& a)
		{
			cout << a._a << endl;
		}
	};
private:
	int _a;
};

int main()
{
	A a;
	A::B b;
	b.func(a);
	return 0;
}

这里的B类就是定义在A类中,此时B就是A的友元,可以访问A类中的私有 

A类的实现就是专门为B准备的

匿名对象

先来看个代码

class A {
public:
	int func(int n) {
		//...
		return n;
	}
};

int main()
{
	A a;
	a.func(1);
	return 0;
}

如果要调用func函数我们可以先定义一个A类对象,然后再使用func

那还有什么更好的调用方式吗?

我们可以使用匿名对象来调用这个func函数

class A {
public:
	int func(int n) {
		//...
		return n;
	}
};

int main()
{
	A().func(1);
	return 0;
}

这里的A()就是一个匿名对象,它的生命周期只有这一行,这一行之后就会结束,所以目的也就只是想调用func函数不那么麻烦且少一点空间

编译器优化

class A {
public:
	A(int a = 1)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}

	A(const A& a)
		:_a(a._a)
	{
		cout << "A(const A& a)" << endl;
	}

	~A()
	{
		cout << "~A()" << endl;
	}
private:
	int _a;
};

int main()
{
	A a1(10);
	A a2 = a1;
	return 0;
}

明明有两个成员变量a1、a2,怎么才调用一次构造函数呢?

这里就是由于编译器的优化,由于A a2 = a1这个表达式做了两件事情,一件就是构造,另一件就是拷贝构造,所以编译器优化成了只有拷贝构造即可

越是新版本的编译器优化的会越狠

将Debug模式换成Release也会有更多的优化

这些都是为了加快代码运行的效率


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

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

相关文章

【人工智能】在未来智慧城市的建设及应用分析

作者主页: 知孤云出岫 目录 作者主页:案例分析&#xff1a;人工智能在未来智慧城市的建设及其影响和应用引言一、人工智能在智慧城市中的关键应用领域 案例分析&#xff1a;人工智能在未来智慧城市的建设及其影响和应用 引言 智慧城市是利用信息和通信技术&#xff08;ICT&am…

【开源 Mac 工具推荐之 2】洛雪音乐(lx-music-desktop):免费良心的音乐平台

旧版文章&#xff1a;【macOS免费软件推荐】第6期&#xff1a;洛雪音乐 Note&#xff1a;本文在旧版文章的基础上&#xff0c;新更新展示了一些洛雪音乐的新功能&#xff0c;并且描述更为详细。 简介 洛雪音乐&#xff08;GitHub 名&#xff1a;lx-music-desktop &#xff09;…

政安晨【零基础玩转各类开源AI项目】基于Ubuntu系统部署Hallo :针对肖像图像动画的分层音频驱动视觉合成

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 零基础玩转各类开源AI项目 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 本文目标&#xff1a;在Ubuntu系统上部署Hallo&#x…

Linux——Shell脚本和Nginx反向代理服务器

1. Linux中的shell脚本【了解】 1.1 什么是shell Shell是一个用C语言编写的程序&#xff0c;它是用户使用Linux的桥梁 Shell 既是一种命令语言&#xff0c;有是一种程序设计语言 Shell是指一种应用程序&#xff0c;这个应用程序提供了一个界面&#xff0c;用户通过这个界面访问…

香橙派AIpro-携手华为-为AI赋能

文章目录 香橙派AIpro-携手华为-为AI赋能开箱和功能介绍开箱功能介绍 环境搭建镜像烧录进入系统 测试项目YOLOv5部署YOLOv5识别单张图片实时识别视频使用Ascend测试yolov5 产品评价 香橙派AIpro-携手华为-为AI赋能 今天新入手了一款香橙派AIPro&#xff0c;让我们一起跟着文章…

ELK日志分析系统部署文档

一、ELK说明 ELK是Elasticsearch&#xff08;ES&#xff09; Logstash Kibana 这三个开源工具组成&#xff0c;官方网站: The Elastic Search AI Platform — Drive real-time insights | Elastic 简单的ELK架构 ES: 是一个分布式、高扩展、高实时的搜索与数据分析引擎。它…

适用于618/7xx芯片平台 AT开发 远程FOTA升级指南教程

简介 AT版本的远程升级主要是对AT固件版本进行升级&#xff0c;实际方式为通过合宙官方IOT平台升级或者使用自己搭建的服务器进行升级服务。 该文档教程流程适用于 618/716S/718P 芯片平台的Cat.1模块 合宙IOT平台配置 升级日志 —— 如何查看 升级日志 —— 响应码列表 响应…

算法之判断对称二叉树

94. 二叉树的中序遍历101. 对称二叉树 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;fa…

国产大模型速度测评,第一名竟然是它。。。

原文首发&#xff1a;国产大模型速度测评&#xff0c;第一名竟然是它。。。经过一段时间调研&#xff0c;我选择了一些国内比较知名的大模型进行速度测评&#xff0c;按照模型参数量及API调用价格&#xff0c;分为了三个档次&#xff0c;分别对应经济型、高性价比型、旗舰型。h…

【Django】网上蛋糕商城后台-商品管理

1.商品管理功能 当管理员点击商品管理时&#xff0c;发送服务器请求 path(admin/goods_list/, viewsAdmin.goods_list), # 处理商品列表请求 def goods_list(request):try:type request.GET["type"]except:type 0try:ym request.GET["ym"]except:ym …

【视频讲解】神经网络、Lasso回归、线性回归、随机森林、ARIMA股票价格时间序列预测|附代码数据

全文链接&#xff1a;https://tecdat.cn/?p37019 分析师&#xff1a;Haopeng Li 随着我国股票市场规模的不断扩大、制度的不断完善&#xff0c;它在金融市场中也成为了越来越不可或缺的一部分。 【视频讲解】神经网络、Lasso回归、线性回归、随机森林、ARIMA股票价格时间序列…

Haproy服务

目录 一.haproxy介绍 1.主要特点和功能 2.haproxy 调度算法 3.haproxy 与nginx 和lvs的区别 二.安装 haproxy 服务 1. yum安装 2.第三方rpm 安装 3.编译安装haproxy 三.配置文件详解 1.官方地址配置文件官方帮助文档 2.HAProxy 的配置文件haproxy.cfg由两大部分组成&…

【MQTT(3)】开发一个客户端,QT-Android安卓手机版本

手机版本更加方便 生成安卓库 参考了这个代码 在编译Mosquitto以支持安卓平台时&#xff0c;主要涉及到使用Android NDK&#xff08;Native Development Kit&#xff09;进行交叉编译。环境的准备参考之前的博客【QT开发&#xff08;17&#xff09;】2023-QT 5.14.2实现Andr…

jenkins添加ssh证书

1、生成ssh密匙&#xff1a;windows生成ssh密匙-CSDN博客 2、添加添加ssh凭证&#xff1a;jenkins路由地址为&#xff1a;/manage/credentials/store/system/domain/_/ 点击添加凭证 选择第二个&#xff0c;将生成的私匙 id_rsa 里边的内容赋值到密钥&#xff0c;id留空自动…

使用小波分析实现文字种类自动识别

文章目录 数据简介开始实验小波分解得出结果结果分析误差分析 数据简介 各找一篇中文&#xff0c;日文&#xff0c;韩文&#xff0c;英文&#xff0c;俄文较长的学术论文。将论文转化为JPG格式。拆分每张JPG生成更多小的JPG。最终获得很多5个不同语言的JPG并且自带标签。数据链…

网安速查引擎(厂商设备大全)

速查引擎 斯元的速查引擎以其全面、精准的信息整合和便捷的搜索功能&#xff0c;大大缩短了用户查找相关厂商和产品信息的时间&#xff0c;从而提高了工作效率和决策质量。用户可以轻松查阅到各个赛道中的领先厂商和最新技术&#xff0c;帮助企业快速找到适合的合作伙伴和解决方…

逆向案例二十五——webpack所需模块函数很多,某翼云登录参数逆向。

解决步骤&#xff1a; 网址&#xff1a;aHR0cHM6Ly9tLmN0eXVuLmNuL3dhcC9tYWluL2F1dGgvbG9naW4 不说废话&#xff0c;密码有加密&#xff0c;直接搜索找到疑似加密位置打上断点。 再控制台打印&#xff0c;分析加密函数 有三个处理过程&#xff0c;b[g]得到的是用户名,b[f] 对…

HiFi-GAN——基于 GAN 的声码器,能在单 GPU 上生成 22 KHz 音频

拟议的 HiFiGAN 可从中间表征生成原始波形 源码地址&#xff1a;https://github.com/NVIDIA/DeepLearningExamples 论文地址&#xff1a;https://arxiv.org/pdf/2010.05646.pdf 研究要点包括 **挑战&#xff1a;**基于 GAN 的语音波形生成方法在质量上不及自回归模型和基于流…

Linux部署Prometheus+Grafana

【Linux】PrometheusGrafana 一、Prometheus&#xff08;普罗米修斯&#xff09;1、Prometheus简述2、Prometheus特点3、Prometheus生态组件4、Prometheus工作原理 二、部署Prometheus1、系统架构2、部署Prometheus3、修改配置文件4、配置系统启动文件 三、部署 Node Exporter …

Spring MVC-什么是Spring MVC?

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 今天你敲代码了吗 文章目录 1.MVC定义2. Spring MVC 官方对于Spring Web MVC的描述这样的: Spring Web MVC is the original web framework built on the Servlet APl and has been includedin the Spring Frame…