C++/语法@初始化列表

目录

  • 初始化列表
    • 特征
    • 疑惑
    • 区别
    • 必在初始化列表中初始化的三种成员变量
      • 1、引用成员变量
        • 程序例子:
        • 运行结果:
      • 2、const成员变量
        • 程序例子:
        • 运行结果:
      • 3、自定义类型成员(没有默认构造函数的类)
        • 程序例子:
        • 运行结果:
    • 总结

初始化列表

特征

以一个冒号开始 ;
以一个逗号作为数据成员的分割标志 ;
在每个成员变量后面跟个括号,括号内放入初始值或表达式 ;

如下:

class Date
{
public:// 构造函数Date(int year = 0, int month = 1, int day = 1):_year(year), _month(month), _day(day)				// 初始化列表{}private:int _year;int _month;int _day;
};

注意
1、每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

2、有3种成员变量,必须放在初始化列表位置中进行初始化。哪3种变量在下面再做讲解。



疑惑

在学习之前,有没有这么一个疑问:前面学习 构造函数 的时候,如下:

class Date
{
public:// 构造函数Date(int year = 0, int month = 1, int day = 1){_year = year;_month = month;_day = day;}private:int _year;int _month;int _day;
};

  不是已经实现了对象变量的初始化了吗?怎么又蹦出来个初始化列表一说,跟前面学的构造函数的初始化有什么区别吗?

答案是,两者是有所区别的。
首先,什么叫初始化?或者说初始化的文意是什么?初始化的含义是只运行一遍、只赋值一次…,即一次以后就没有初始化的什么事了。但是在前面学习的构造函数中,如下:

	// 构造函数Date(int year = 0, int month = 1, int day = 1){_year = year;_month = month;_day = day;_year = 2023;_month = 12;_day = 13;}

我们可以在构造函数里面,对要初始化的变量,多次赋值,而这明显在概念上与初始化相违背。
  确切的说,这种方式,构造函数内的语句只能称为赋初值,但不能称为初始化。因为初始化只能初始化 ( 赋值 ) 一次,而这里的构造函数体内可以多次赋值。这便是两者在概念上的差别



区别

前面了解了两种 “初始化” 概念上的的区别后,那么有个问题,在功能上、实际效果中,两者有没有什么区别呢?

我们发现,不管是使用初始化列表:

	// 构造函数Date(int year = 0, int month = 1, int day = 1):_year(year), _month(month), _day(day)				// 初始化列表{}

进行初始化,还是使用:

	// 构造函数Date(int year = 0, int month = 1, int day = 1){_year = year;_month = month;_day = day;}

这种方式实现“初始化”,这里两者在功能实现上,并没有什么区别啊。
是的,对于有些类型的成员变量,不管是放在初始化列表中进行初始化,还是放在构造函数体内进行初始化,两者在实际效果上并没有什么区别。
但是 ! 如前面特征中注意事项中的第二点提到的,有3种类型的成员变量,必须放在初始化列表中进行初始化。

下面对这3种类型依个进行学习。



必在初始化列表中初始化的三种成员变量

  在类中成员变量为以下三种类型之一时,初始化必须要在初始化列表中才能够让编译器自动去初始化变量。



1、引用成员变量

当类中的成员变量类型为引用时,如下:

程序例子:
class A
{
public:// 可以理解成初始化列表是对象的成员变量的定义的地方A(int ref = 0):_ref(ref){}void print(){cout << _ref << endl ;}
private:// 成员变量的声明int& _ref;		// 引用};int main()
{A a1;a1.print();return 0;
}

运行结果:

在这里插入图片描述

收入眼帘的,我们发现在初始化列表中初始化过的引用成员变量_ref 并不是我们所想的初始化为0啊。这是因为,
引用成员变量_ref在初始化列表中,引用的是构造函数中的形参,形参缺省值是0,当程序运行到初始化列表中_ref的值确实是被初始化为0的,我们通过调试可以发现,结果如下:

在这里插入图片描述


但是出了构造函数后,形参ref在构造函数作用域内时开辟的空间将随着出了构造函数的作用域而被销毁,空间使用权归还编译器,这时 _ref 引用的空间依旧是原先形参开辟的空间,但出了构造函数后那块空间已经归还编译器了,因此那块空间将是随机值,所以打印的结果是随机值。

要想如我们所构想的,给引用成员变量_ref 初始化为0,那么我们要先给 _ref 提供一个有效的对象。这个对象并不会在出了构造函数后就被销毁,如:

class A
{
public:static int s;A():_ref(s){}void print(){cout << _ref << endl;}
private:// 成员变量的声明int& _ref;		// 引用};int A::s = 0;
int main()
{A a1;a1.print();A::s = 10;a1.print();return 0;
}

我们在类中声明了一个静态整形变量s,然后将_ref 初始化为静态整形变量s的别名,同时在类外,对s定义初始化为0。这时就完成了对 引用成员变量 _ref 的初始化,即绑定到静态对象s的地址上。

运行结果:

在这里插入图片描述

结果显示,当我们对静态整形变量s更改时,引用成员变量 _ref 的值也跟着更改。

那么如果,我们将引用成员变量 _ref 放在构造函数体内进行初始化呢?

	static int s;A(){_ref = s;}

编译器会报错,编译不通过。具体是为什么,我是这么理解的
  首先,引用的特性是在创建时必须被初始化。在private中只是引用声明,还没有被创建,而当用类创建出对象时,编译器自动调用构造函数,对成员变量初始化。
  其次,程序是先运行到初始化列表,而后才进入函数体内的。当运行到函数体内时,引用成员变量已经被创建出来了,而被创建出来后又没有对其初始化,即给定一个对象(在C++中对象和变量是等效的说法)。这违背了引用的特性,所以编译器会报错。
所以要在引用成员变量被创建之前,即在初始化列表中,对引用成员变量进行初始化。
可以这么理解,在初始化列表中 "_ref(s) " 时,引用成员变量_ref被创建出来并且作为了静态对象s的引用。而程序运行如果出了初始化列表进入函数体内时,已经完成了对引用成员变量 _ref 的创建了。


以上,便是必须在初始化列表中初始化的成员变量类型之一,引用成员变量。接下来,对下一个成员变量类型进行学习。






2、const成员变量

程序例子:
class B
{
public:// 可以理解成初始化列表是对象的成员变量的定义的地方B(int a = 0):_n(a){}void print(){cout<< _n << endl;	}
private:// 成员变量的声明const int _n;   // const
};int main()
{B b;return 0;
}



运行结果:

在这里插入图片描述

原因和引用成员变量类似,也是因为const关键字的特性: const 变量在声明时必须被初始化,因为它们一旦被赋值之后就不能再修改。
所以当程序跑到构造函数体内时,const修饰的成员变量 _n 已经被创建出来了,但是又没有初始化,所以编译器会报错。
因此同样的,对于const修饰的成员变量的初始化,必须在初始化列表中。

下面学习最后一种必须在初始化列表中初始化的成员变量类型。






3、自定义类型成员(没有默认构造函数的类)

对于自定义类型成员,并且自定义类型是没有默认构造函数的类,就是只有需要传参才能对对象进行初始化的构造函数。如下:

程序例子:
class A
{
public:A(int a):_a(a){}int Getnum(){return _a;}
private:int _a;
};class B
{
public:// 可以理解成初始化列表是对象的成员变量的定义的地方B(int a, int ref):_aobj(1){}void print(){cout<< _aobj.Getnum() << endl;}
private:// 成员变量的声明A _aobj;		// 没有默认构造函数(需要传参才可以调用的构造函数)
};int main()
{B b;b.print();return 0;
}

  程序中,类B中的有一个成员变量_aobj,类型是自定义类型类A。在类A中,有一个显示构造函数,但是因为该构造函数参数没有缺省值,因此要想调用这个构造函数时,必须得传参数。即类A中是一个没有默认构造函数(不用传参数就能调用的构造函数 )的类。

  这种情况下,要想对类B中的成员变量_aobj 初始化时,必须得放到初始化列表中进行i初始化。先看看程序运行的结果,



运行结果:

在这里插入图片描述

结果如期所至,对象_aobj 的成员变量被初始化成1。

  至于为什么没有默认构造函数的自定义类型的变量,初始化时必须放在初始化列表中,按照我的理解,和前面的原因是一致的:
因为程序跑到类B中构造函数体内时,成员变量类A实例出的对象 _aobj 已经被创建出来了,而创建出来时又没有给对象_aobj传入参数,又因为类A中没有默认构造函数,所以对象_aobj调用构造函数失败,即类B的成员变量_aobj初始化失败,导致类B实例的对象b初始化失败,因此编译器报错。

  如果还有疑问:命名函数体内已经 " _aobj(1); " 传参数初始化了啊?为什么说没给对象_aobj传参呢?
对于这个疑问,可以理解为,当程序运行到构造函数体内的 " _aobj(1);" 时( 当_aobj初始化是放在构造函数体内,而不是初始化列表时 ),对象_aobj已经在初始化列表处的位置被创建出来了, 创建出来的时候又因为没有传入参数,而对象_aobj的类型A中又没有默认构造函数,所以对于对象_aobj调用类A的构造函数进行初始化时,编译出错。


至此,对于必须放在初始化列表中进行初始化的类型,介绍完毕。


如果到此还不太理解,可以联想以上三种类型,在普通函数或者main函数中,定义(创建)时的要求是什么?可以理解为是一样的概念。如下:

class C
{
public:C(int c): _c(c){}
private:int _c;
}int main()
{const int a;	// 编译能通过吗?int& b;		    // 编译能通过吗?class C;        // 编译能通过吗?return 0;
}

以上在main函数中,创建出来的三个变量,a、b、c 能被编译器顺利通过吗?这个问题就留给你们自己去思考了。






总结

1、对于大多数类型的成员变量,初始化时放在初始化列表也行,放在构造函数体内也罢,效果都是一样的;

2、对于以上特殊的三种成员变量类型,必须放在初始化列表中进行初始化;

注意事项
成员变量的初始化顺序就是成员变量在类中声明的顺序,跟在初始化列表中的先后次序无关



建议

1、对于所有的成员变量的初始化,都放在初始化列表中进行初始化。因为 C++ 的设计中,构造函数的初始化列表就是专门用于初始化成员变量的地方。将所有成员变量的初始化都放在初始化列表中有助于提高代码的清晰度和一致性。

2、初始化列表中成员变量初始化的先后次序,与类中成员变量的声明顺序保持一致,有助于代码的可观读性。

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

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

相关文章

mysql:通过INFORMATION_SCHEMA数据库查询表的元信息

使用SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA database_name AND TABLE_NAME table_name;查询某个表的元信息。其中database_name替换为数据库名称&#xff0c;table_name替换为表的名称。 例如&#xff0c;下面语句&#xff0c;查询development数据库中…

Mirrors and reflections for VR

专为虚拟现实而建,但也非常适合非虚拟现实桌面和移动项目 这是URP管道,从Unity2019.4.16一直测试到2023年。 完全工作场景预览,轻松修改着色器材质。着色器支持折射,可以制作很酷的效果。 镜子/反射可以互相反射,而不仅仅是2...想象一下一个电梯,3面镜子都互相反射,直到…

阿木实验室普罗米修斯项目环境配置

引言 普罗米修斯项目其实只是个大ROS功能包&#xff0c; 里面每个模块就是每个ROS功能包&#xff0c;比如控制模块&#xff0c;视觉模块等等。对PX4配置的与这个一样&#xff0c;另外他是使用自己的P系列无人机&#xff08;我个人是&#xff30;450&#xff09;&#xff0c;所…

腾讯科技Hi Tech Day暨2023数字开物大会:智能涌现将通往无数的未来

腾讯科技讯 12月14日&#xff0c;以“智能涌现 数开万物”为主题的腾讯科技Hi Tech Day暨2023数字开物大会在北京国家会议中心召开&#xff0c;腾讯科技邀请知名院士、知名经济学家、知名大学教授、研究院院长、产业大咖、互联网大厂高管、知名科技领域头部企业高管、产业数字化…

vscode创建代码片段 vue3modal

{// Place your 全局 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope// is left empty or omitte…

干货:企业如何讲好品牌故事

品牌故事讲得好&#xff0c;不仅能够体现品牌特色还能向消费者传递品牌精神的重要工具&#xff0c;优秀的品牌故事能够促进产品销量&#xff0c;为品牌带来曝光率&#xff0c;今天媒介盒子就来和大家聊聊&#xff1a;如何讲好品牌故事。 一、 品类历史和故事 品牌虽然是新品牌…

平面腔体谐振计算与仿真

PCB的电源网络是由电介质材料隔开的两个平行金属板所组成&#xff0c;可以通过以下的3种方法对其谐振模式进行分析&#xff1a; 1. 基于腔体模型的计算&#xff1b; 2. 基于SPICE等效电路&#xff1b; 3. 基于全波数值电磁算法的3D模型。 设计得当的前提下&#xff0c;上述3种方…

LVS负载均衡器(nat模式)+nginx(七层反向代理)+tomcat(多实例),实现负载均衡和动静分离

目录 前言 一、配置nfs共享存储 二、配置2个nginx节点服务的网页页面 节点1:192.168.20.10 步骤一&#xff1a;修改网关指向调度器的内网ip地址 步骤二&#xff1a;将nfs共享的目录进行挂载&#xff0c;并修改nginx的配置文件中location的root指向挂载点 步骤三&#xff…

SpringCloud面试题——分布式事务

一&#xff1a;什么是分布式事务? 分布式事务是指在分布式系统中涉及到多个数据库或多个应用程序之间的事务处理&#xff0c;这些数据库或应用程序可能分布在不同的物理节点上&#xff0c;甚至可能位于不同的地理位置。在分布式事务中&#xff0c;需要确保所有参与者的事务操…

PPT插件-好用的插件-图形缩放-大珩助手

图形缩放 包括适合屏幕、适合宽度、适合高度、水平翻转、垂直翻转、指定角度&#xff0c;可同时对多个形状进行操作 适合屏幕 一键设置图像、文本、形状的长宽尺寸与当前幻灯片一致 适合宽度 一键设置图像、文本、形状的宽度尺寸与当前幻灯片一致 适合高度 一键设置图像…

智能优化算法应用:基于驾驶训练算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于驾驶训练算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于驾驶训练算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.驾驶训练算法4.实验参数设定5.算法结果6.参考…

DDOS攻击方式有哪些,要如何防护

DDOS攻击我们也称之为流量攻击&#xff0c;分布式拒绝服务攻击(英文意思是Distributed Denial of Service&#xff0c;简称DDOS&#xff09;于不同位置的多个攻击者同时向一个或数个目标发动攻击&#xff0c;或者一个攻击者控制了位于不同位置的多台机器并利用这些机器对受害者…

Python3开发笔记(简洁版)

一、开发编辑器 1. pycharm 2. IDLE&#xff08;Python自带软件&#xff09; 方法&#xff1a;Microsoft Store搜索 Python 安装 二、数据类型 Python中有以下几种主要的数据类型&#xff1a; 数字&#xff08;Numbers&#xff09;、 字符串&#xff08;Strings&#xff09…

2023年全国职业院校技能大赛信息安全管理与评估赛项正式赛(模块一)GZ032

全国职业院校技能大赛高等职业教育组 信息安全管理与评估 任务书 模块一 网络平台搭建与设备安全防护 极安云科专注技能竞赛&#xff0c;包含网络建设与运维和信息安全管理与评估两大赛项&#xff0c;及各大CTF&#xff0c;基于两大赛项提供全面的系统性培训&#xff0c;拥…

【入坑指南】| OpenCV4.8 + CUDA + 扩展模块支持编译

大家好&#xff0c;今天给大家分享一下最新版本OpenCV4.8如何编译支持CUDA加速&#xff0c;实现深度学习模型部署速度提升。 软件版本支持 CMake3.13 或者以上版本 https://cmake.org/ VS2017专业版或者以上版本 3050ti CUDA11.3 OpenCV4.8源码包 https://github.com/opencv…

三层交换的原理

一.三层交换技术 1.什么是三层交换机 要实现vlan间通信&#xff0c;就需要路由&#xff0c;解决办法要么是二层交换机加路由器形成单臂路由&#xff0c;要么就是直接使用三层交换机。 ①什么是单臂路由&#xff1a; ②单臂路由实现不同vlan间通信的原理&#xff1a; 路由器…

JVM的类的生命周期

目录 前言 1. 加载&#xff08;Loading&#xff09;&#xff1a; 2. 验证&#xff08;Verification&#xff09;&#xff1a; 3. 准备&#xff08;Preparation&#xff09;&#xff1a; 4. 解析&#xff08;Resolution&#xff09;&#xff1a; 5. 初始化&#xff08;Ini…

Ubuntu解决Failed to fetch https://... Could not resolve ‘某个源‘

在我使用sudo apt install subversion的时候遇到报错&#xff1a; 这个报错与Ubuntu操作系统的软件源配置文件有关系。错误提示显示无法解析“mirrors.shanhe.com”地址&#xff0c;这可能是由于更新软件包列表或下载软件包时出现的网络问题。 1.可以先更新一下源试试&#xf…

Vue 子传父 组件传参 defineEmits

defineEmits 属性&#xff1a;用于创建自定义事件&#xff0c;接收子组件传递过来的数据。 注意&#xff1a;如果自定义事件的名称&#xff0c;和原生事件的名称一样&#xff0c;那么只会触发自定义事件。 defineEmits 仅适用于 setup 语法糖&#xff0c;其它写法请见&#x…

SpringBoot+SSM项目实战 苍穹外卖(5)(Redis入门)

继续上一节的内容&#xff0c;本节学习Redis&#xff0c;并实现营业状态设置功能。 目录 Redis环境搭建Redis数据类型Redis常用命令在Java中操作Redis环境搭建java操作常见类型数据 店铺营业状态设置设置营业状态管理端查询营业状态用户端查询营业状态swagger区分管理端和用户端…