深入篇【C++】类与对象:再谈构造函数之初始化列表与explicit关键字
深入篇【C++】类与对象:再谈构造函数之初始化列表与explicit关键字
- Ⅰ.再谈构造函数
- ①.构造函数体赋值
- ②.初始化列表赋值
- 【<特性分析>】
- 1.至多性
- 2.特殊成员必在性
- 3.必走性:定义位置
- 4.一致性
- 5.不足性
- Ⅱ.explicit关键字
- ①.隐式类型转化
- ②.作用
Ⅰ.再谈构造函数
我们知道在创建对象时,编译器会通过调用构造函数,给对象中各个成员变量一个合适的初始值。
也就是我们可以通过构造函数来给对象中的成员变量赋值。不过给成员变量赋值其实有两种方式,一种就是在函数体内进行赋值,另一种是在初始化列表赋值。函数体内赋值我们是知道什么意思,那什么叫初始化列表呢?
我们知道创建一个对象,什么表示对象创建出来了呢?
对象实例化表示对象已经创建出来,这是对象整体定义的地方,然后对象就会调用构造函数进行初始化。
对象定义的地方是对象实例化,实例化后就会调用构造函数初始化。
那想一想对象成员变量是在哪里定义的呢?
对象实例化只是对对象整体定义的地方,而初始化列表才是对象成员变量定义的地方。
只有定义完后才可以初始化。
所以初始化列表是对象成员定义的地方。
①.构造函数体赋值
对象实例化后就会调用构造函数初始化对象。
在函数体内部进行赋值初始化。
```handlebarsclass Date
{
public:Date(int year = 1, int month = 1, int day = 1){_year = year;_month = month;_day = day;}void Print()const{cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
虽然上述构造函数调用之后,对象种已经有一个初始值,但是不能将其称为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。
因为初始化只能初始化一次,但构造函数体内可以多次赋值。
本质上来说是因为对象成员变量不在函数体内定义,所以赋值后也不叫作初始化,只有在定义的地方赋值才可以叫做初始化,而对象成员变量定义的地方其实是初始化列表。
②.初始化列表赋值
初始化列表:以一个冒号开始,接着就是以一个逗号分割数据成员列表,每个成员变量后面跟上一个括号,括号里是初始值或者表达式。
class Date
{
public:Date(int year = 1, int month = 1, int day = 1)//这个就叫做初始化列表:_year(year)//以一个冒号开始,注意后面没有分号,_month(month)//逗号分割,_day(day)//每个成员变量后面都有一个括号,括号里是初始值或表达式{}
private:int _year;int _month;int _day;
};
【<特性分析>】
1.至多性
每个成员变量在初始化列表中至多出现一次,也可以不出现。
因为定义完后再初始化,而初始化只能初始化一次。
不出现的话那就会在函数体内进行初始化。
2.特殊成员必在性
类中包含以下成员时,必须放在初始化列表位置进行初始化。
- 引用成员变量
- const成员变量
- 自定义类型成员(且该类没有默认构造函数时)
我们一个一个分析,为什么上面三个成员必须放在初始化列表初始化。
引用成员变量和const成员变量有什么特别之处呢?为什么会被要求放在初始化列表初始化呢?
引用成员变量和const成员变量都有一个特点:那就是在定义的时候必须初始化。
不然编译器会报错,而初始化列表正是变量定义的地方,在定义的地方给初始值才能成功的对引用成员变量和const成员变量初始化。如果在函数体内部进行赋值初始值,那这样不是初始化,因为函数体内部不是它们定义的地方,仅仅给个赋值是不能完成初始化的。
class B
{
public:B(int a, int ref)//初始化列表:成员变量定义的地方:_ref(ref)//引用, _n(1)//const修饰的{}
private:int& _ref;//引用成员变量//这两个特征就是必须在定义的时候就初始化const int _n;//const修饰的成员变量
};
第三种成员变量是自定义类型成员,并且当类中没有默认构造函数时,自定义类型成员必须放在初始化列表初始化,这是为什么呢?
class A
{
public:A(int a=0 )//有默认构造函数:_a(a){cout << "A(int a = 0)" << endl;}
private:int _a;
};
class B
{
public://初始化列表:对象的成员定义的地方B(int a, int ref):_ref(ref)//引用, _n(1)//const修饰的{}
private:A _obj;//有默认构造函数时,可以不用初始化int& _ref;const int _n;int _x = 1;
};
我们知道编译器生成的默认构造的工作是对自定义类型初始化,对内置类型不做处理。
所以当有默认构造函数时,自定义类型我们就不用去再初始化了。因为没有参数我也可以调用构造函数初始化。那没有默认构造函数呢?我们是不是就得手动给自定义类型成员变量进行初始化。但要注意的是函数体内不允许没有默认构造的自定义类型成员变量初始化,必须在初始化列表初始化。

道理其实是一样的,自定义类型在定义的时候也要进行初始化。
那怎么初始化呢? —调用构造函数。
如果有默认构造函数,那就可以之间使用默认构造函数初始化。
如果没有默认构造函数,那就必须在初始化列表进行初始化,因为初始化列表是成员变量定义的地方,如果要求在定义的时候进行初始化,那么必须得在初始化列表进行,函数体内部不是定义的地方,只是可以进行赋初始值,如果在函数体内部进行"初始化"其实是定义和赋初始值分开了,没有做到定义只是进行赋初始值。
而对于那些没有要求在定义时必须初始化的变量,既可以在初始化列表进行初始化,也可以在函数体内部进行初始化,可以做到定义和赋初始值分开。
class A
{
public:A(int a )//这个不是默认构造,这个是需要传参的构造函数:_a(a){cout << "A(int a = 0)" << endl;}
private:int _a;
};
class B
{
public://初始化列表:对象的成员定义的地方B(int a, int ref):_ref(ref)//引用, _n(1)//const修饰的,_obj(a)//自定义类型,无默认构造。{}
private:A _obj;//没有默认构造函数时,必须在初始化列表进行初始化。int& _ref;//引用const int _n;//const修饰的int _x = 1;};
要区分默认成员函数和默认构造函数:
1.默认成员函数是C++规定的几种特殊的成员函数,是不写编译器可以自动生成的,有默认构造函数,默认拷贝函数,默认赋值函数等等。而默认构造函数是包含在内的。
2.默认构造函数有三种:总的特征就是不用传参就可以使用的函数。
无参的构造函数+全缺省的构造函数+编译器生成的默认构造函数都叫做默认构造函数。
3.并且默认构造函数只能有一个。
所以当自定义类型成员变量类型没有默认构造函数时(三种默认构造函数,自己写的带有参数构造函数)在函数体内部赋初始值是无法通过的,必须在初始化列表显式初始化,去调用自己写的构造函数初始化。
也就是当没有提供默认构造函数的自定义类型,必须在初始化列表初始化。
3.必走性:定义位置
尽量使用初始化列表初始化,为什么呢?
因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会显示有初始化列表初始化,并且所有成员变量都会经过初始化列表的。因为初始化列表是成员变量定义的地方。
每个成员变量在初始化列表至多出现一次,但也可以不出现,不出现的意思是不给定义的变量赋初始值,但这个变量是在初始化列表定义的,只不过没有给初始值,其实所有的成员变量都会走初始化列表。
class B
{
public://初始化列表:对象的成员定义的地方B(int a, int ref):_ref(ref)//引用, _n(1)//const修饰的,_obj(a),_x(2)//对于那些没有要求必须在定义时初始化的既可以在初始化列表初始化{//_x=2;//也可以在函数体内部初始化}
private:A _obj;int& _ref;const int _n;int _x ;//初始化列表没有显式定义_x就会使用这个缺省值
};
比如上面的内置类型_x既可以在初始化列表初始化,又可以在函数体内部初始化,在初始化列表初始化就是定义时就初始化了,而在函数体内部初始化就是在初始化列表定义完后到函数体内赋初始值完成初始化。
class B
{
public://初始化列表:对象的成员定义的地方B(int a, int ref):_ref(ref)//引用, _n(1)//const修饰的,_obj(a){}
private:A _obj;int& _ref;const int _n;int _x=1 ;//这个1是缺省值,缺省值是给初始化列表的
你们还记得缺省值吧,C++给默认构造函数打补丁就规定了可以在成员变量声明时给缺省时,这样对于内置类型,也可以完成初始化了,那现在看来这个缺省值是如何完成初始化的呢?
其实这个缺省值就是给初始化列表用的,因为每个成员变量都会走初始化列表,初始化列表会将缺省值赋值给已经定义好的成员变量,这样成员变量就完成了初始化了。
当显式的初始化时,初始化列表会优先选取显式给的初始值,而放弃掉缺省值,当没有显式的初始化时,初始化列表就会将缺省值赋值给成员变量 。
4.一致性
成员变量在类中声明次序就是在其初始化列表中的初始化顺序。
声明的顺序对应着要初始化的顺序,必须要一致,不然会出现问题。
比如下面这个问题:
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();
}
这个结果是什么呢?

为什么呢?注意到成员变量声明的次序,_a2先声明,_a1后声明,则表明_a2先进行初始化,_a1后进行初始化

5.不足性
初始化列表的使用虽然很方便,但也有它不足之处,比如当有些赋值后的成员需要检查是否赋值成功,再比如要求对数组进行初始化,初始化列表就无法完成这样的工作。
class Stack
{
public:Stack(int capacity=10):_a((int*)malloc(sizeof(int)* capacity)),_top(0),_capacity(capacity){if (_a == nullptr){perror("malloc");//要求数组初始化一下memset(_a, 0, sizeof(int) * capacity);}}
private:int* _a;int _top;int _capacity
};
再比如写一个动态二维数组:初始化列表就无法完成这样的工作,必须借助函数体来解决,所以总有一些工作是初始化列表完成不了的,这时就需要和函数体一起协同工作了。
class AA
{
public:AA(int row = 10, int col = 5):_row(row), _col(col){_a = (int**)malloc(sizeof(int*) * row);for (int i = 0; i < row; i++){_a[i] = (int*)malloc(sizeof(int) * col);}}
private:int** _a;int _row;int _col;
};
Ⅱ.explicit关键字
①.隐式类型转化
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值,其余均有默认值的构造函数,还具有类型转化的作用。
这其实就是隐式类型转化,看下面代码:
int main()
{int i = 0;double d1 = i;//这里发生了什么呢?
}
这里i是int类型,d1是double类型将i赋值给d1,会发生类型转化。发生什么样子的转化呢?
其实在这个过程中会生成一个临时变量,将i赋给临时变量,临时变量再赋给d1.

class A
{
public:A(int a):_a(a){}A(const A& a1){_a = a1._a;}
private:int _a;
};
int main()
{A aa1(1);A aa2 = 4;
}
对象aa1调用构造函数初始化成1,那对象aa2在干嘛呢?
对象aa2其实是将整形的4赋给aa2,这里就涉及了隐式类型转化,将int类型隐式转换成A类型。
这里其实是4调用构造函数,构造一个A类型的临时对象,然后临时对象再拷贝构造给aa2。只不过编译器将这两步优化成一步,优化成直接构造。
如果不相信的话可以看看下面的代码:
int main()
{A aa1(1);A& aa2 = 4;
}
用对象aa2来引用4可以吗?
这里肯定不行呀,这两个类型都不一样肯定不能引用。
但只要给对象aa2前面加上const修饰那么这样就可以引用了,这是为什么呢?

不知道你还记得临时变量具有常性这个特点吗?
正是因为这个特点,加上const修饰后就允许引用了,因为4会调用构造函数,构造出一个A类型的临时变量,而因为临时变量具有常性,相比较正常的A类型权限缩小了,所以不能相互引用,但一旦加上const修饰后,权限相同,就可以相互引用了。
所以在隐式类型转化时会产生一个临时变量的。
②.作用
如果不想在调用构造函数时构造出一个临时变量的话,就可以用关键字explicit。
使用explicit后构造函数就无法构造出临时变量了,这样的隐式类型转化就不会发生了。
class A
{
public:explicit A(int a):_a(a){}A(const A& a1){_a = a1._a;}
private:int _a;
};
int main()
{A aa1(1);A aa2 = 4;
}

相关文章:
深入篇【C++】类与对象:再谈构造函数之初始化列表与explicit关键字
深入篇【C】类与对象:再谈构造函数之初始化列表与explicit关键字 Ⅰ.再谈构造函数①.构造函数体赋值②.初始化列表赋值【<特性分析>】1.至多性2.特殊成员必在性3.必走性:定义位置4.一致性5.不足性 Ⅱ.explicit关键字①.隐式类型转化②.作用 Ⅰ.再谈…...
广东棒球发展建设·棒球1号位
一、概述 棒球是一项源于美国的运动,自20世纪初开始传入中国,近年来在广东省的发展也逐渐受到关注。本文将就广东棒球的发展现状及未来发展方向进行分析。 二、发展现状 目前广东省内棒球赛事主要有以下几种: 1. 业余棒球联赛:…...
浅谈PMO对组织战略的支持︱美团骑行事业部项目管理中心负责人边国华
美团骑行事业部项目管理中心负责人边国华先生受邀为由PMO评论主办的2023第十二届中国PMO大会演讲嘉宾,演讲议题:浅谈PMO对组织战略的支持。大会将于6月17-18日在北京举办,更多内容请浏览会议日程 议题内容简要: 战略是组织运行的…...
互联网医院资质代办|互联网医院牌照的申请流程
随着互联网技术的不断发展,互联网医疗已经逐渐成为人们关注的热点话题。而互联网医院作为互联网医疗的一种重要形式,也越来越受到社会各界的关注。若想开展互联网医院业务,则需要具备互联网医院牌照。那么互联网医院牌照的申请流程和需要的资…...
网络:DPDK复习相关知识点_2
1.RTC运行至完成时模式,单核单模块 2.pipeline模式,多核多模块,每个模块都是一个处理引擎,但会有缓存一致性问题 3.Mbuff数据包内存操作对象,相当于是数据包的一个索引,对网络的处理都集中在这个Buff上 …...
阿里云大学考试Java中级题目及解析-java中级
阿里云大学考试Java中级题目及解析 1.servlet释放资源的方法是? A.int()方法 B.service()方法 C.close() 方法 D.destroy()方法 D servlet释放资源的方法是destroy() 2.order by与 group by的区别? A.order by用于排序,group by用于排序…...
【星戈瑞】Sulfo-CY3-COOH磺化/水溶性Cyanine3羧酸1121756-11-3
Sulfo-CY3 COOH是一种荧光染料,其分子结构中含有COOH官能团,最大吸收波长为550纳米左右,可以通过分光光度计等设备进行检测。Sulfo-CY3 COOH是一种带有羧基的荧光染料,可以与含有氨基的生物分子通过偶联反应形成共价键,…...
Java NIO和IO的主要区别
当学习了Java NIO和IO的API后,一个问题马上涌入脑海: 我应该何时使用IO,何时使用NIO呢?在本文中,我会尽量清晰地解析Java NIO和IO的差异、它们的使用场景,以及它们如何影响您的代码设计。 下表总结了Java N…...
SQL查询语句
DQL语句--排序查询 # 格式: select * from 表名 order by 要排序的列1 [asc/desc], 要排序的列2 [asc/desc]; # 解释: # 1. 无论SQL语句简单或者是复杂, order by语句一般都放最后, 注意: 如果有limit(分页), 则它(limit)在最后. # 2. asc表示升序, desc表示降序, 其中, 默…...
四象限法进程调度
周二收到一篇推送 一次云上网络毫秒级的优化与实践,很有意义的实践和探索,建议阅读,文章不长,没有冗长的源码分析,结论很清晰。 谈谈我的看法。 多少有种感觉,Linux 越来越像个响应系统而不是服务器。 虚…...
蓝桥杯拿到一等奖,并分享经验
昨天和群里的小伙伴在群里聊,有的小伙伴竟然说蓝桥杯一等奖没有含量,我也是醉了! 就像去年看了一个号主写的:研究生遍地都是! 放眼全国14亿人口,别说研究生了,本科生占比有多少? “蓝桥杯是我人生中得到…...
vue3。 Cannot use JSX unless the ‘–jsx’ flag is provided. ts(17004)
react用tsx或者jsx很常见,也有配套的配置 那如果是vue呢? 默认是没问题的,可是我用了jsdoc,并开启了checkjs,然后vscode就爆红了 谷歌,百度,一个晚上 查到的答案: 推荐我新增tsco…...
HVV面试题目总结
蓝队 如何识别安全设备中的无效告警? 常见的端口有哪些? 这些端口对应的服务是什么? 针对这些服务,红队攻击方式有哪些? 常用的威胁情报平台有哪些? 有没有做过关于情报输出的工作? 木马驻留系统的方式有哪些? 当收到钓鱼邮件的时候,说说处置思路…...
Access denied for user ‘root‘@‘localhost‘ (using password:YES) 解决方案
文章目录 问题描述解决方案: 问题描述 Access denied for user ‘root’‘localhost’:拒绝用户’root’localhost’的访问。 出现这个报错语句的一般原因是输入了错误的密码,也有可能是是root帐户默认不开放远程访问权限。 相关的解决方法是重新设置…...
为什么C++这么复杂还不被淘汰?
C是一门广泛使用的编程语言,主要用于系统和应用程序的开发。尽管C具有一些复杂的语法和概念,但它仍然是编程界的重量级选手,在编程语言排行榜中一直位居前列。为什么C这么复杂还不被淘汰呢? C有以下优势 1、C具有高性能 C是一门编…...
内存泄漏的原因,内存泄漏如何避免?内存泄漏如何定位?
1. 内存溢出 内存溢出 OOM (out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个int,但给它存了long才能存下的数,那就是内存溢出。 2. 内存泄…...
关于全志T113开发板接7寸LCD屏幕显示异常问题的解决方案
在入手全志T113之后,第一时间移植好了之前6ull平台的rootfs。但是在测试QT的过程中发现屏幕最右侧有一部分显示不正常,经过初步推测应该是RGB行场同步时序有问题。本以为在设备树里面稍作修改之后就能OK,但是居然前前后后一共花了至少三个星期…...
SpringMVC第四阶段:Controller中如何接收请求参数
Controller中如何接收请求参数 1、原生API参数类型 1.1、HttpServletRequest类 只需要在Controller的目标方法中, 直接写上HttpServletRequest对象即可获取 原生API的 request对象实例。 RequestMapping(value "/p1") public String param1(HttpServletRequest …...
第三十回: LisvtView响应事件
我们在上一章回中介绍了如何给ListView添加分隔线,本章回中将介绍ListView响应事件相关的知识.闲话休提,让我们一起Talk Flutter吧。 概念介绍 我们在这里说的ListView响应事件主要分两种类型: 一种是滑动ListView时ListView做出响应,我们…...
重磅!用友荣登全球5强
近日,全球权威信息技术研究和顾问公司Gartner发布《Market Share: All Software Markets, Worldwide,2022》报告,用友在EAM(资产管理)市场再创新高,市场占有率位居全球第五位,亚太第一位&#x…...
SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
【无标题】湖北理元理律师事务所:债务优化中的生活保障与法律平衡之道
文/法律实务观察组 在债务重组领域,专业机构的核心价值不仅在于减轻债务数字,更在于帮助债务人在履行义务的同时维持基本生活尊严。湖北理元理律师事务所的服务实践表明,合法债务优化需同步实现三重平衡: 法律刚性(债…...
