变量初始化和赋值
本节阅读量:上一节中,介绍了如何定义用于存储值的变量。本课中,将探索如何将值放入变量并使用。
如下一段代码,首先分配一个名为x的整数变量,再分配两个名为y和z的整数变量:
|
|
变量赋值
变量定义后,使用「=」运算符(在单独语句中)为其赋值。将值写入的过程称为赋值,「=」运算符称为赋值运算符。
|
|
默认情况,赋值将=右侧的值复制到左侧的变量。称为「拷贝赋值」。
下面是使用赋值两次的示例:
|
|
打印:
|
|
当值7赋给变量width时,先前存在的值5被覆盖。
警告
新手程序员最常见的错误之一是混淆赋值运算符(=)和相等运算符(==)。赋值(=)用于将值赋值给变量。相等(==)用于测试两个操作数的值是否相等。
初始化
赋值的一个特点是至少需要两个语句:赋值之前必须存在一条变量定义语句。
两个步骤结合使用。定义变量时,同时为变量提供初始值。称为初始化。用于初始化变量的值称为初始值。
C++中初始化非常复杂,这里进行简单说明。
C++中有6种基本的变量初始化方法:
|
|
花括号以不同间距编写。是否使用额外空格提高可读性,依个人偏好而定。
默认初始化
没有提供初始化值时(例如上面的变量a),称为默认初始化。多数情况,默认初始化,变量的值会不确定。
拷贝初始化
等号后有初始值,称为拷贝初始化。这种形式是从C继承的。
|
|
与赋值相似,将等式右侧的值拷贝到左侧变量中。上面代码中,变量 width 初始化为值5。
拷贝初始化在现代C++中已不受欢迎,对于某些复杂类型,效率低于其他形式的初始化。较旧的代码会用(特别是从C移植的代码),或有些开发人员认为它更自然,更易读。
对于高级读者
当隐式复制或转换值时,会使用拷贝初始化,例如按值将参数传递给函数、按值从函数返回或按值捕获异常。
直接初始化
当在括号内提供初始值设定项时,这称为直接初始化。
|
|
最初引入直接初始化是为了更有效地初始化复杂对象(那些具有class类型的对象)。由于被列表初始化取代,就像拷贝初始化一样,直接初始化在现代C++中已不受欢迎。然而,列表初始化有一些奇怪的特性,因此直接初始化在某些情况下再次应用。
对于高级读者
当值显式转换为另一类型时,使用直接初始化。
直接初始化很难区分变量和函数定义,这是它不受欢迎原因之一。例如:
|
|
列表初始化
现在C++中初始化对象的方法,是利用花括号初始化形式:列表初始化(也称为统一初始化或大括号初始化)。
列表初始化有三种形式:
|
|
列表初始化还有一个好处:不允许“缩小转换范围”。意味着,如果尝试使用变量不能安全保存的值,编译器将产生错误。例如:
|
|
如上,将具有小数部分(.5部分)的数字(4.5)分配给整数变量(只能容纳没有小数部分的数字)。
拷贝和直接初始化只会删除小数部分,而将值4初始化到变量width(编译器会发出警告,因为很少需要丢失数据)。然而,列表初始化时,编译器将生成错误,迫使纠正问题。
列表初始化允许在不丢失潜在数据的情况下转换。
总之,与其他相比,列表初始化目前更受欢迎,因为在大多数情况下可以工作,它不允许缩小转换范围,且它支持使用值列表进行初始化(将在以后课程介绍)。
旁白…
引入列表初始化前,某些类型的初始化需要使用拷贝初始化,而其他类型的初始化则需要使用直接初始化。引入列表初始化是为了提供在大多数情况下都有效的更一致的初始化语法(因此有时被称为“统一初始化”)。
此外,列表初始化提供了使用值列表初始化对象的方法(因此被称为“列表初始化”)。
值初始化和零初始化
当用空大括号对变量列表初始化时,进行值初始化。大多数情况,值初始化将变量初始化为零(或空,如果这更适合给定类型)。发生归零的这种情况,称为零初始化。
|
|
Q: 应该何时用{0}vs{}?
如果实际使用初始化的值,则显式设置初始化值。
|
|
如果值在使用前被替换,请使用值初始化。
|
|
初始化变量
有些场景(比如为了关键代码性能),定义变量时,不去初始化变量,也是允许的。
在1.6节中,将探讨如果尝试使用没有明确定义值的变量会发生什么——未初始化的变量和未定义的行为。
最佳做法
创建变量时初始化变量。
初始化多个变量
上一节中注意到,用逗号分隔名称,可在单个语句中定义相同类型的多个变量:
|
|
其实,最佳实践是避免这种语法。然而,由于可能会遇到这种风格的其他代码,再多谈谈是有用的。
初始化在同一行上定义的多个变量:
|
|
尝试用一个初始化语句来初始化两个变量时,可能会出现一个常见的陷阱:
|
|
在顶部语句中,变量“a”将保持未初始化状态,编译器可能会告警,也可能不会告警。这是让程序间歇性崩溃的好方法。
考虑直接初始化或大括号初始化的情况:
|
|
由于括号或大括号通常放在变量名的右边,使值5仅用于初始化变量b和d,而不是a或c,更易发现错误。
