【C++】-类和对象之初始化列表(explicit的简单介绍)(下)

news2024/10/6 6:45:21

💖作者:小树苗渴望变成参天大树
❤️‍🩹作者宣言:认真写好每一篇博客
💨作者gitee:gitee
💞作者专栏:C语言,数据结构初阶,Linux,C++

文章目录

  • 前言


前言

经过前面的好几篇博客,大家应该感受到类和对象的难度所在了吧,c++之所以难学,就是因为有许多细节,我们不小心就会出错,虽然难学,但我们要克服,迎难而上,接下来我们来学习类和对象的其他知识点,这里面有点知识点还是不太好理解的,所以我打算分好几篇博客来写,这样大家看的就不会太乱,这篇我们来讲解初始化列表


大家还记得我们之前的构造函数是怎么写的吗?我们有三个默认构造函数,还有半缺省的和不缺省的我们来看:(以日期类为例)

date(int year,int month,int day)//这是不缺省的构造函数
{
 	_year=year;
 	_month=month;
 	_day=day;
}

这种构造函数其实是初始化的其中一种方式:函数体内赋值
我们另一种初始化的方式是:初始化列表
再此之前我们其实已经接触过初始化列表,只是之前不知道而已

date()
{}

date(int year)
{}

date(int year,int month,int day)
{}

这些可以认为是初始化列表,只是列表里面什么都没有。

我们来写一个类:

class date
{
public:
	friend ostream& operator<<(ostream& cout, const date& d);
private:
    //成员变量的声明
	int _year;
	int _month;
	int _day;
};
date d1;//对象的整体定义

我们看到再创建对象的时候,是对对象整体的定义,那么成员变量的定义再哪里呢??
初始化列表就是成员变量定义的地方也就是初始化的地方,函数体只是对定义好并且为初始化的变量进行赋值,例如:

1.
int a;//初始化列表
a=10;//函数体内赋值

2.
int b=10;//初始化列表

对于第一种就相当于函数体内赋值,初始化列表什么也不写,第二种就是把内容写出来,例如:

1.
date()
{a=10;}

2.
date()
:a(10)
{}

如果函数体内不赋值,初始化列表不写,也会对成员变量进行定义,只是不初始化,你写了肯定就是初始化了,没办法单独写一个变量,例如:

date()
:a
{}//这种是不行的

相信大家对初始化列表有了一定的认识,我们开始正式介绍初始化列表

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。

class Date
{
public:
Date(int year, int month, int day)
     : _year(year)//这才是定义并初始化
     , _month(month)
     , _day(day)
 {}
private:
int _year;
int _month;
int _day;
};

【注意】

  1. 每个成员变量在初始化列表中最多只能出现一次(初始化只能初始化一次)
  2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
    引用成员变量
    const成员变量
    自定义类型成员(且该类没有默认构造函数时)

我们先来看看前两个

class B
{
public:
 B(int a, int ref)
 :_ref(ref)
 ,_n(10)
 {}
private:
 int& _ref;  // 引用
 const int _n; // const 
};

我们的引用和const有一个共同的特性就是必须初始化
我们再上面说过初始化列表是成员变量的定义的地方,所以我们只能使用初始化列表,不能使用函数体内赋值,原因是函数体内赋值是再定义之后的行为,初始化是在定义时候的行为

这种写法不完全正确,我们说过引用是变量的别名,那么我们再初始化列表引用的是形参ref,出了函数就会被销毁,那么这个引用就没有任何意义,我们应该传引用,外面传变量:

 B(int a, int& ref)
 :_ref(ref)
 ,_n(10)
 {}

再括号里面可以写自己的参数,也可以传固定的值

class A
{
public:
	A(int a)
	{_a=a;}
private:
 int _a;
};
class B
{
public:
 B(int a, int ref)
 :_aobj(a)

 {}
private:
 A _aobj;  // 没有默认构造函数
};

对于自定义类型,再构造函数的时候,我们会调用本身的默认构造函数,默认构造是不需要传参数就可以调用的,所以在初始化列表可以什么都不用写,但是没有默认构造函数的时候,我们必须就要进行传参的初始化,再定义A对象的时候,直接调用:A a(20),但是对于B我们要定义对象的时候不能直接传参给_aobj变量进行初始化,只能通过初始化列表,才能达到要求,但是你有默认构造的时候就不需要写。

可以这么简单来说,初始化列表替代了90%的函数体内赋值能完成的事情。

初始化列表完成不了的事:
我们来看看之前的栈类:

typedef int DateType;
class stack
{
public:
stack(int capacity=4)
:_array(( DataType*)malloc(sizeof(DataType)*capacity))
  , _capacity(capacity)
   ,_size(0)
{
   if(_array=NULL)//检查
   {
   		perror("malloc:");
   }
   memset(_array,0,sizeof(DataType)*capacity);//初始化为0
}

private:
 DataType* _array;
 int _capacity;
 int _size;
};

大家看到有些必须在函数体内才能完成操作

初始化列表的优点之处:
我们在之前使用两个栈实现一个队列

class myqueue
{
public:
	myqueue(int capacity)
	:_push(capacity)_pop(capacity)
	{}
private:
	stack _push;
	stack _pop;
};

这两个栈我想初始化我想要的容量,通过本身myqueue自身的构造函数去调用栈里面构造函数打不到我们想要的效果,只有通过初始化列表去完成
在这里插入图片描述
大家应该看到效果了,其实初始化列表对于自定义类型回去调用它本身的默认构造,对于内置类型在给缺省值的时候也会做相应的处理:
在这里插入图片描述
优先使用你初始化列表里面的值,没有就使用缺省值,就相当于备胎。

初始化列表的细节:
我们来看下这个代码:

class A
{
public:
    A(int a)
       :_a1(a)
       ,_a2(_a1)
   {}
    
    void Print() {
        cout<<_a1<<" "<<_a2<<endl;
   }
private:
    int _a2;
    int _a1;
};
int main() {
    A aa(1);
    aa.Print();
}
A. 输出1  1
B.程序崩溃
C.编译不通过
D.输出1  随机值

在这里插入图片描述
我们看看,居然不是输出1 1,而出现了一个随机值,原因是初始化列表的初始顺序是根据成员变量的位置来进行初始话,在类中 _a2在_a1上面,所以先初始化_a2,此时_a1是随机值,所以_a2是随机值,_a1在定义初始化变成了1

成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

所以初始化列表的顺序应该和成员变量声明的顺序一致就不会出什么问题

explicit关键字:
我们来看一个类:

class Date
{
public:
	Date(int year)
	:_year(year)
	{}
private:
 int _year;
 int _month;
 int _day;
};

Date d1(2023);

上面的代码大家应该看得懂,给d1的年进行初始化为2023
但我要这么写呢?d1=2024;

这里面存在隐式类型转换,在类型转换的时候,会创建临时变量,对于这个,整型会转换成自定义类型,先构造一个对象,在拷贝构造给d1。

现在的编译器会优化成直接构造,我们把拷贝构造写出来,看看是不是进行优化了

在这里插入图片描述
但是此转换仅仅适用于只需要传一个参数的对象。
在这里插入图片描述
大家应该可以明白我想要表达的意思了吧

怎么证明它发生了类型转换呢,之前说过有隐式类型转换,会创建临时变量,临时变量具有常性,我们用引用来看看:
在这里插入图片描述
上面是权限的放大,下面是权限的平移,希望大家可以理解。如果不想发生这种转换,我们就使用explicit关键字
在这里插入图片描述
这个关键字在后面学到String类的时候可能会用到,大家想要具体了解可以看一下这篇博客explicit

对于今天的重点主要是初始化列表,以后我们大部分使用的都是初始化列表,现在不咋熟练没事,经常使用就好,下篇我将讲解static成员,大家也复习复习我讲的const成员。细节非常多,大家要理解,我们下篇再见

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

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

相关文章

SpringSecurity自定义实现手机短信登录

SpringSecurity自定义登录验证-手机验证码登录 其实实现原理上跟账号密码登录一样的 1、自定义短信验证Token 定义一个仅使用手机号验证权限的鉴权Token&#xff0c;SpringSecurity原生的UsernamePasswordAuthenticationToken是使用username和password&#xff0c;如下图 pr…

向量时钟算法

向量时钟不仅同步本进程的时钟值&#xff0c;而且还同步已知的其他进程时钟值 分布式系统中每个进程Pi保存一个本地逻辑时钟向量值VCi&#xff0c;VCi(j)代表进程Pi知道的进程Pj的本地逻辑时钟值 初始化VCi向量为[0,…]进程Pi每发生一次事件&#xff0c;VCi[i]加一进程Pi给进…

应付模块无法关账问题 APP-AR-11332 您必须在关闭此期之前过账其中的所有事务处理

问题描述 AR关账时遇到了这个问题&#xff0c;根本原因是&#xff0c;因为用户录入另一个贷项的发票&#xff0c;做过核销&#xff0c;后来又取消了核销&#xff0c;未创建会计分类&#xff0c;未传送总&#xff0c;不想要这个贷项发票了&#xff0c;前台删除不了&#xff0c;…

经验分享,api 接口设计原则有这几条

结合我多年在 API 行业摸爬滚打的经验&#xff0c;我总结了一下&#xff0c;API 接口设计原则有这几条&#xff1a; 接口设计应该简单易用&#xff0c;易于理解和使用&#xff1b; 接口设计应该支持多种格式&#xff0c;如JSON、XML等&#xff1b; 接口设计应该支持多种请求方…

渲染速度慢,使用云渲染会快多少?

设计师在使用软件制作效果图和动画师在制作动画时&#xff0c;其中有一个比较关键的环节就是渲染成像&#xff0c;渲染的效率主要跟使用的电脑显卡或CPU性能有关&#xff0c;如果性能太低&#xff0c;渲染的速度会很慢&#xff0c;拉长了项目整体的交付周期&#xff0c;云渲染速…

Vite + Vue3 实现前端项目工程化

Vue3 发布至今&#xff0c;周边的生态、技术方案已足够成熟&#xff0c;个人认为新项目是时候切换到 Vite Vue3 了。今天就给大家操作一下这种技术方案实现前端工程化。 1. 初始化项目 通过官方脚手架初始化项目 第一种方式&#xff0c;这是使用vite命令创建&#xff0c;这种…

FM33A048B SPI1/2

概述 芯片的2 个SPI 接口模块SPI1 和SPI2&#xff0c;可配置为主设备或从设备&#xff0c;实现与外部的SPI 通信。 特点&#xff1a; ⚫ 全双工3线串行同步收发 ⚫ 2路独立通道 ⚫ 主从模式 ⚫ 可编程时钟极性和相位 ⚫ 可编程比特速率 ⚫ 从模式最大频率为FAHBCLK/2 ⚫ 传输结…

Hello算法——笔记

文章目录 1 引言算法数据结构算法和数据结构的关系 2 复杂度分析时间复杂度空间复杂度 3 数据结构数据与内存数据结构分类 4 数组与链表 参考资料 1 引言 算法 算法是一组用于解决特定问题或执行特定任务的明确定义的计算步骤或指令集合。算法可以被视为一种解决问题的方法或…

【iOS】--手势操作

文章目录 UIGestureRecognizer 的继承关系&#xff1a; 使用手势步骤UIPanGestureRecognizer&#xff08;拖动&#xff09;UIPinchGestureRecognizer(拖动&#xff09;UIRotationGestureRecognizer&#xff08;旋转&#xff09;UITapGestureRecognizer&#xff08;点按&#xf…

浅谈造纸配电室环境监控系统的应用案例

摘要&#xff1a;智能配电室环境监控系统可实现自动巡检、自动预警等功能&#xff0c;减少人员到现场巡视次数&#xff0c;能及早发现设备的潜在风险&#xff0c;迅速检测故障&#xff0c;节约维护保养时长&#xff0c;为配电生产检修、运行、各业务的标准化、规范化管理提供有…

【Flowable】Flowable候选人和候选人组

在流程定义中在任务结点的 assignee 固定设置任务负责人&#xff0c;在流程定义时将参与者固定设置在.bpmn 文件中&#xff0c;如果临时任务负责人变更则需要修改流程定义&#xff0c;系统可扩展性差。针对这种情况可以给任务设置多个候选人或者候选人组&#xff0c;可以从候选…

IntelliJ IDEA编辑模板变量详解(Edit Template Variables)

函数描述annotated(“annotation qname”)使用驻留在指定位置的注释创建类型符号。 例如&#xff0c;请参见迭代组中的活动模板。anonymousSuper()为Kotlin对象表达式建议一个超类型。arrayVariable()建议当前作用域中适用的所有数组变量。 例如&#xff0c;请参见迭代组中的活…

ACM - DP习题集(word里面的一小部分题集)

DP 一、经典问题1、编辑距离2、扔鸡蛋问题3、整数背包4、最大独立集5、最长公共子序列6、最长公共递增子序列7、最长公共子串&#xff08;ing&#xff09;8、最长上升子序列9、最长回文子序列10、最长回文子串&#xff08;ing&#xff09;11、最长不重复子字符串&#xff08;in…

OpenGL教程中矩阵Matrix的介绍

变换 原文Transformations作者JoeyDeVries翻译Django, Krasjet, BLumia校对暂未校对 尽管我们现在已经知道了如何创建一个物体、着色、加入纹理&#xff0c;给它们一些细节的表现&#xff0c;但因为它们都还是静态的物体&#xff0c;仍是不够有趣。我们可以尝试着在每一帧改变…

Python 操作 Excel,如何又快又好?

➤数据处理是 Python 的一大应用场景&#xff0c;而 Excel 则是最流行的数据处理软件。因此用 Python 进行数据相关的工作时&#xff0c;难免要和 Excel 打交道。Python处理Excel 常用的系列库有&#xff1a;xlrd、xlwt、xlutils、openpyxl ◈xlrd &#xff0d; 用于读取 Exce…

import一个“太极”库,让Python代码提速100倍!

众所周知&#xff0c;Python的简单和易读性是靠牺牲性能为代价的—— 尤其是在计算密集的情况下&#xff0c;比如多重for循环。 不过现在&#xff0c;大佬胡渊鸣说了&#xff1a; 只需import 一个叫做“Taichi”的库&#xff0c;就可以把代码速度提升100倍&#xff01; 不信…

0-1规划在编程问题中的应用(UnityC#脚本/折返约瑟夫/OpenGL机器人摆臂循环)

一、0-1规划的定义 百度百科的解释&#xff1a;0-1规划是决策变量仅取值0或1的一类特殊的整数规划。在处理经济管理中某些规划问题时&#xff0c;若决策变量采用 0-1变量即逻辑变量&#xff0c;可把本来需要分别各种情况加以讨论的问题统一在一个问题中讨论。 如上面所说&…

记csdn打不开或打开缓慢后的修复--如何查找dns并修改hosts文件

记csdn打开缓慢后的修复–如何查找dns并修改hosts文件 问题&#xff1a; CSDN文章打开的十分缓慢&#xff0c;经常出现无法打开页面的错误提示 &#xff08;以前用的好好的&#xff0c;现在不知道公司局域网改了什么东西&#xff0c;导致我的电脑打开CSDN经常缓慢好久&#x…

ACM - 其他算法 - 基础(前缀和 + 差分)

ACM- 其他算法 一、前缀和模板例题1、区间余数求K倍区间个数&#xff1a;AcWing 1230. K倍区间例题2、前缀和哈希求最长个数平分子串:Leetcode 面试题 17.05 字母与数字 二、差分1、一维差分2、二维差分 一、前缀和 模板 //一维前缀和 S[i] a[1] a[2] ... a[i] a[l] ... …

【行情速递】MLCC龙头涨价;车厂砍单芯片;台积电28nm设备订单全部取消!

导语&#xff1a;进入第二季度&#xff0c;MLCC龙头三环集团官宣涨价!风华高科紧随其后。车市价格战蔓延至芯片端&#xff0c;车厂开始砍单芯片&#xff0c;短短半年时间不到&#xff0c;车用芯片市场从价格飞涨和一片难求的背景&#xff0c;转为砍单与降价促销...更多详情请阅…