Chapter4 复合类型
4.1 数组
数组(array)是一种数据结构,能够存储多个同类型的值。数组声明应指出以下三点:
- 存储在每个元素中值的类型。
- 数组名。
- 数组中的元素数。
声明数组的通用格式如下:typename arrayName[arraySize]
表达式arraySize
指定元素数目,它必须是整型常数或const
值,也可以是常量表达式,即其中所有的值在编译时是已知的。
数组初始化规则:
只有在定义数组时才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组;
可以使用下标分别给数组中的元素赋值;
如果只对数组的一部分进行初始化,则编译器将把其他元素设置为0;
c++11数组初始化:
初始化数组时,可省略等号(=);
可不在大括号内包含任何东西,这将把所有元素都设置为0。
如
float blances[4]{}
,将float数组balances的所有元素设置为0;列表初始化禁止缩窄转换。
4.2 字符串
C-风格字符串具有一种特殊的性质:以空字符(null character)结尾,空字符写作'\0'
,其ASCII码为0,用来标记字符串的结尾。使用一对引号括起来的字符串被称为字符串常量(string constant)或字符串字面值(string literal),它隐式地包括了结尾的空字符。
字符串常量(使用双引号)不能与字符常量(使用单引号)互换。字符常量是字符串编码的简写表示,字符串常量实际上表示的是字符串所在的内存地址。
istream中的类(如 cin
)提供了一些面向行的类成员函数:getline()
和get()
,这两个函数都读取一行输入,直到到换行符,随后getline()
将丢弃换行符,而get()将换行符保留在输入序列中。
getline()
getline()函数每次读取一行,它通过换行符确定行尾,但不保存换行符,在存储字符串时,它使用空字符来替换换行符。
cin.getline()
函数有两个参数,第一个参数是用来存储输入行的数组的名称。第二个参数是要读取的字符数,如果这个参数为n,则函数最多读取n-1个字符,余下的空间用于存储自动在结尾处添加的空字符。get()
istream类还有另一个get()成员函数,该函数有几个重载版本,其中一种版本的工作方式与
getline()
类似,它们接受的参数相同,解释参数的方式也相同,并且都读取到行尾。但get()并不再读取并丢弃换行符,而是将其留在输入队列中。不带参数的cin.get()
调用可读取下一个字符(包括换行符),因此可以用它来处理换行符,为读取下一行输入做准备。cin.get(n, ArSize).get()
。假设使用
get()
将一行输入读入数组,如何直到停止读取的原因是由于已经读取了整行,而不是由于数组已填满呢?查看下一个字符,如果是换行符,说明已经读取了整行;否则说明该行中还有其他输入。空行和其他问题
当
getline()
或get()
读取空行时,将发生什么情况?最初的做法是,下一条输入语句将在前一条getline()
或get()结束读取的位置开始读取;当前的做法是,当get()读取空行后将设置失效位(failbit
),这意味着接下来的输入将被阻断,但可以使用cin.clear()
来恢复输入。
4.3 string类简介
string类提供了将字符串作为一种数据类型的表示方法,它的定义隐藏了字符串的数组性质,可以想处理普通变量那样处理字符串,要使用string类,必须在程序中包含头文件string。
在很多方面使用string对象与使用字符数组相同:
- 可以使用C-风格字符串来初始化string对象;
- 可以使用
cin
来将键盘输入存储到string对象中; - 可以使用
cout
来显式string对象; - 可以使用数组表示法来访问存储在string对象中的字符;
- 可以使用列表初始化语法初始化string对象。
在原始字符串中,字符表示的就是自己,原始字符串将”(和)”用作定界符,并使用前缀R来标识原始字符串。
4.4 共用体
共用体是一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型。共用体的长度位其最大成员的长度。
4.5 枚举
默认情况下,将整数值赋给枚举量,第一个枚举量的值位0,第二个枚举量的值为1,依次类推。
每个枚举都有取值范围,通过强制类型转换,可以将取值范围中的任何整数值赋给枚举变量,即使这个变量不是枚举值。
4.6 指针和自由存储空间
指针是一个变量,其存储的是值的地址,而不是值本身。对于常规变量,只需使用地址运算符&就可以获得其地址。*运算符被称为间接值(indirect value)或接触引用(dereferencing)运算符,将其应用于指针,可以得到该地址处存储的值。
在C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向数据的内存。
为一个数据对象获得并指定分配内存的通用格式如下:typeName * pointer_name = new typeName;
当需要内存是,可以使用new来请求,delete运算符是的在使用完内存后,能够将其归还给内存池。一定要配对地使用new和delete,否则将发生内存泄漏。不能使用delete来释放声明变量所获得的内存。
只能使用delete来释放使用new分配的内存,然而,对空指针使用delete时安全的。
为数组分配内存的通用格式:type_name * pointer_name = new type_name [num_element]
,可以以使用数组名的方法来使用pointer_name
.
使用new和delete时,应遵守如下规则:
- 不要使用delete来释放不是new分配的内存。
- 不要使用delete释放同一个内存块两次。
- 如果使用new [] 为数组分配内存,则应使用delete []来释放。
- 如果使用new[] 为一个实体分配内存,则应使用delete来释放。
- 对空指针使用delete是合法的。
将指针变量加1后,其增加的值等于指向的类型占用的字节数。
在很多情况下,可以以相同的方式使用指针名和数组名,在多数表达式中,它们都表示地址。区别之一时,可以修改指针的值,而数组名是 常量。另一个区别是,对数组应用sizeof运算符得到的是数组的长度,而对指针运用sizeof运算符得到的是指针的长度,即使指针指向的是一个数组。这种情况下,C++不会数组名解释为地址。
使用数组声明来创建数组时,将采用静态联编,即数组的长度在编译时设置;使用new[]运算符创建数组时,将采用动态联编,即将在运行时为数组分配空间,其长度也将在运行时设置。
自动存储、静态存储和动态存储
自动存储
在函数内部定义的常规变量使用自动存储空间,被称为自动变量。它们在所属的函数被调用时自动产生,在该函数结束时自动消亡。(自动变脸是一个局部变量,其作用域为包含它的代码块)
自动变量通常存储在栈中。这意味着执行代码块时,其中的变量将依次加入到栈中,而在离开代码块时,将按相反的顺序释放这些变量(后进显先出,LIFO)。
静态存储
静态存储是整个程序执行期间都存在的存储方式,使变量称为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字static。
动态存储
new和delete运算符提供了一种比自动变量和静态变量更加灵活的方法。它们管理了一个内存池,这在C++中被称为自由存储空间(free store)或堆(heap),该内存池同用于静态变量和自动变量的内存是分开的。
4.7 数组的替代品
4.7.1 模板类vector
模板类vector类似于string类,也是一种动态数组,它使用new和delete管理内存,但这种工作是自动完成的。要使用vector对象,必须包含头文件vector,其次,vector包含在名称空间std中,因此需使用using编译指令、using声明或std::vector
。vector<typeName> vt(n_element)
创建一个名为vt的vector对象,它可存储n_element个类型为typeName的元素,其中参数n_element可以是整型常量,也可以是整型变量。
4.7.2 模板类array
array位于名称空间std,要创建array对象,需要包含头文件array,创建语法如下:array<typeName, n_elem> arr;
该声明创建一个名为arr的array对象,它包含n_elem个类型为typeName的元素。
array对象和数组存储在相同的内存区域(栈)中,而vector对象存储在另一个区域(自由存储区域或堆)中。