Chapter4 复合类型

Chapter4 复合类型

4.1 数组

数组(array)是一种数据结构,能够存储多个同类型的值。数组声明应指出以下三点:

  • 存储在每个元素中值的类型。
  • 数组名。
  • 数组中的元素数。

声明数组的通用格式如下:typename arrayName[arraySize]表达式arraySize指定元素数目,它必须是整型常数或const值,也可以是常量表达式,即其中所有的值在编译时是已知的

数组初始化规则:

  • 只有在定义数组时才能使用初始化,此后就不能使用了,也不能将一个数组赋给另一个数组;

  • 可以使用下标分别给数组中的元素赋值;

  • 如果只对数组的一部分进行初始化,则编译器将把其他元素设置为0;

  • c++11数组初始化:

    1. 初始化数组时,可省略等号(=);

    2. 可不在大括号内包含任何东西,这将把所有元素都设置为0。

      float blances[4]{},将float数组balances的所有元素设置为0;

    3. 列表初始化禁止缩窄转换。

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时,应遵守如下规则:

  1. 不要使用delete来释放不是new分配的内存。
  2. 不要使用delete释放同一个内存块两次。
  3. 如果使用new [] 为数组分配内存,则应使用delete []来释放。
  4. 如果使用new[] 为一个实体分配内存,则应使用delete来释放。
  5. 对空指针使用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::vectorvector<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对象存储在另一个区域(自由存储区域或堆)中。