章节目录

变量初始化和赋值

本节阅读量:

上一节中,介绍了如何定义用于存储值的变量。本课中,将探索如何将值放入变量并使用。

如下一段代码,首先分配一个名为x的整数变量,再分配两个名为y和z的整数变量:

1
2
int x;    // 定义一个类型为int的变量x
int y, z; // 定义两个类型为int的变量y与z

变量赋值

变量定义后,使用「=」运算符(在单独语句中)为其赋值。将值写入的过程称为赋值,「=」运算符称为赋值运算符。

1
2
3
4
int width; // 定义一个类型为int的变量width
width = 5; // 将值5写入width中

// 变量width现在的值是5

默认情况,赋值将=右侧的值复制到左侧的变量。称为「拷贝赋值」。

下面是使用赋值两次的示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <iostream>

int main()
{
	int width;
	width = 5; // 将值 5 拷贝赋值到变量 width

	std::cout << width << std::endl; // 打印出 5

	width = 7; // 将width中的值改变为 7

	std::cout << width << std::endl; // 打印出 7

	return 0;
}

打印:

1
2
5
7

当值7赋给变量width时,先前存在的值5被覆盖。


初始化

赋值的一个特点是至少需要两个语句:赋值之前必须存在一条变量定义语句。

两个步骤结合使用。定义变量时,同时为变量提供初始值。称为初始化。用于初始化变量的值称为初始值。

C++中初始化非常复杂,这里进行简单说明。

C++中有6种基本的变量初始化方法:

1
2
3
4
5
6
7
8
int a;         // 默认初始化
int b = 5;     // 拷贝初始化
int c( 6 );    // 直接初始化

// 列表初始化 (C++11引进)
int d { 7 };   // 直接列表初始化
int e = { 8 }; // 拷贝列表初始化
int f {};      // 值列表初始化

花括号以不同间距编写。是否使用额外空格提高可读性,依个人偏好而定。


默认初始化

没有提供初始化值时(例如上面的变量a),称为默认初始化。多数情况,默认初始化,变量的值会不确定。


拷贝初始化

等号后有初始值,称为拷贝初始化。这种形式是从C继承的。

1
int width = 5; // 将 5 拷贝初始化,设置到变量 width

与赋值相似,将等式右侧的值拷贝到左侧变量中。上面代码中,变量 width 初始化为值5。

拷贝初始化在现代C++中已不受欢迎,对于某些复杂类型,效率低于其他形式的初始化。较旧的代码会用(特别是从C移植的代码),或有些开发人员认为它更自然,更易读。


直接初始化

当在括号内提供初始值设定项时,这称为直接初始化。

1
int width( 5 ); // 直接将值 5 设置到变量 width

最初引入直接初始化是为了更有效地初始化复杂对象(那些具有class类型的对象)。由于被列表初始化取代,就像拷贝初始化一样,直接初始化在现代C++中已不受欢迎。然而,列表初始化有一些奇怪的特性,因此直接初始化在某些情况下再次应用。


列表初始化

现在C++中初始化对象的方法,是利用花括号初始化形式:列表初始化(也称为统一初始化或大括号初始化)。

列表初始化有三种形式:

1
2
3
int width { 5 };    // 直接列表初始化,将变量width设置为 5
int height = { 6 }; // 拷贝列表初始化,将变量height设置为6
int depth {};       // 值初始化

列表初始化还有一个好处:不允许“缩小转换范围”。意味着,如果尝试使用变量不能安全保存的值,编译器将产生错误。例如:

1
int width { 4.5 }; // error: int类型无法装下分数

如上,将具有小数部分(.5部分)的数字(4.5)分配给整数变量(只能容纳没有小数部分的数字)。

拷贝和直接初始化只会删除小数部分,而将值4初始化到变量width(编译器会发出警告,因为很少需要丢失数据)。然而,列表初始化时,编译器将生成错误,迫使纠正问题。

列表初始化允许在不丢失潜在数据的情况下转换。

总之,与其他相比,列表初始化目前更受欢迎,因为在大多数情况下可以工作,它不允许缩小转换范围,且它支持使用值列表进行初始化(将在以后课程介绍)。


值初始化和零初始化

当用空大括号对变量列表初始化时,进行值初始化。大多数情况,值初始化将变量初始化为零(或空,如果这更适合给定类型)。发生归零的这种情况,称为零初始化。

1
int width {}; // 值初始化 / 零初始化,将变量width设为 0

初始化变量

有些场景(比如为了关键代码性能),定义变量时,不去初始化变量,也是允许的。

在1.6节中,将探讨如果尝试使用没有明确定义值的变量会发生什么——未初始化的变量和未定义的行为。


初始化多个变量

上一节中注意到,用逗号分隔名称,可在单个语句中定义相同类型的多个变量:

1
int a, b;

其实,最佳实践是避免这种语法。然而,由于可能会遇到这种风格的其他代码,再多谈谈是有用的。

初始化在同一行上定义的多个变量:

1
2
3
4
5
int a = 5, b = 6;          // 拷贝初始化
int c( 7 ), d( 8 );        // 直接初始化
int e { 9 }, f { 10 };     // 直接列表初始化
int g = { 9 }, h = { 10 }; // 拷贝列表初始化
int i {}, j {};            // 值初始化

尝试用一个初始化语句来初始化两个变量时,可能会出现一个常见的陷阱:

1
2
3
int a, b = 5; // 错误 (a 没有被初始化!)

int a = 5, b = 5; // 正确

在顶部语句中,变量“a”将保持未初始化状态,编译器可能会告警,也可能不会告警。这是让程序间歇性崩溃的好方法。

考虑直接初始化或大括号初始化的情况:

1
2
int a, b( 5 );
int c, d{ 5 };

由于括号或大括号通常放在变量名的右边,使值5仅用于初始化变量b和d,而不是a或c,更易发现错误。


1.2 对象与变量

上一节

1.4 iostream:cout、cin和endl简介

下一节