首页 C++ Primer Plus-第4章 复合类型
文章
取消

C++ Primer Plus-第4章 复合类型

    今天看了看复合类型,并不难,我发现每天看完C++都比前一天晚一会,今天快五点半才看完,昨天看到5点。而且这几天都没看games101了TWT,昨晚打了一晚上三国杀,但当内奸赢了一把很爽就是了。

    第四章比较长,分两天看完吧。

    没想到最后是花了三天,10号差几页没看完,11号课比较多,一点没看,拖到了12号……

4.1 数组

  • sizeof运算符返回类型或数据对象的长度(单位为字节)。用于数组名,得到的是整个数组的字节数,用于数组元素,则是元素的长度

  • {}初始化

    • {}初始化列表只能用于数组初始化,不能用于后续赋值

    • 如果使用{}初始化数组的一部分,那么编译器会把其他元素设置为0

    • c++11使用{}:

      • 可以省略=

      • {}不包含内容,则所有元素设置为0

      • 列表初始化禁止缩窄转换

4.2 字符串

  • C++两种处理字符串方式:C风格字符串、string类库

  • C风格字符串:

    • 存储在char数组中

    • 以空字符结尾,\0,ASCII码为0

  • 可以用字符串常量来初始化char数组,比如"abc"\0会自动加到结尾,如果还有空余位置,会自动设置为\0

  • 有时候,字符串很长,无法放到一行中。C++允许拼接字符串字面值,即将两个用引号括起的字符串合并为一个。事实上,任何两个由空白(空格、制表符和换行符)分隔的字符串常量都将自动拼接成一个。

  • 面向行的输入:

  • getline(),通过回车键输入的换行符来确定输入结尾

    • cin.getline(name,20)数组名,要读取的字符数(最多读取的数目,数组其余位置添加空字符)
  • get(),与getline相似,唯一的不同是不再读取并丢弃换行符,而是留在输入队列中,所以需要额外使用一次get()来吸收换行符

    • cin.get(name,20)
  • 可以连续get或getline(因为函数会返回一个cin对象)

    • cin.get(name,20).get();//有点酷,因为外行可能看不懂哈哈
  • get的优点:

    • get使输入更精准,使用get可以知道停止读取的原因是由于读取了整行还是已经填满,只需要查看下一个输入字符是否为换行符。但getline因为会自动吸收换行符,所以无论如何,下一个输入字符都不会是换行符
  • 空行问题,cin读取字符串后,会把回车键生成的换行符留在输入队列,导致后面的getline或者get会认为是一个空行,所以会把空字符串赋给数组

4.3 string类简介

  • 使用string类时,某些操作比数组简单:

    • 不能将一个数组赋给另一个数组,但可以将一个string对象赋给另一个string对象

    • 简化字符串合并,使用+

  • getline的不同用法:

    • cin.getline(charr,10)函数时istream类的一个类方法(cin是istream的一个对象)

    • getline(cin,str),string的用法,说明函数不是类方法

  • 其他形式字符串字面值:wchar_t、char16_t、char32_t,C++分别使用前缀L、u、U来表示

    1
    
    wchar_t title[] = L"abc def";
    
  • C+11还支持Unicode字符编码方案UTF-8。:在这种方案中,根据编码的数字值,字符可能存储为1~4个八位组。C++使用前缀u8来表示这种类型的字符串字面值。

  • C++11新增的另一种类型是原始(raw)字符串。在原始字符串中,字符表示的就是自己。

    1
    
    cout<<R"(Jim"King" Tutt uses "\n" instead of endl.)"
    
    • 输入原始字符串时,按回车键不仅会移到下一行,还将在原始字符串中添加回车字符。

    • 如果想打印)"呢?只需要在"(之间添加其他字符,当然结尾处也要加,相当于使用新的一个字符串来表示开头和结尾,比如R"+*(表示开头,)+*"表示结束

4.4 结构简介

  • 结构是C++OOP堡垒(类)的基石

  • C++11结构使用{}初始化

    • 等号可选

    • 不包含内容则所有成员设置成0

    • 不允许缩窄

  • 可以使用赋值运算符=将结构赋给另一个同类型的结构

4.5 共用体

  • 共用体(union)是一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型。也就是说:结构可以同时存储int、long和double,共用体只能存储int、long或double。所以,共用体的长度为其最大成员的长度

  • 用途之一:当数据项使用两种或更多种格式(但不会同时使用)时,可节省空间。

  • 匿名共用体(anonymous union)没有名称,其成员将成为位于相同地址处的变量。显然,每次只有二个成员是当前的成员

  • 共用体通常用于(但并非只能用于)节省内存

4.6 枚举

  • enum spectrum {red,orange,yellow,green,blue,violet,indigo,ultraviolet};

  • 让spectrum成为新类型的名称;spectrum被称为枚举(enumeration),就像struct变量被称为结构一样。

  • 将red、orange、yellow等作为符号常量,它们对应整数值0~7。这些常量叫作枚举量(enumerator).在默认情况下,将整数值赋给枚举量,第一个枚举量的值为0,第二个枚举量的值为1,依次类推。

  • 在不进行强制类型转换的情况下,只能将枚举量赋给枚举的变量,不能直接赋值数字

  • 强制类型转换:band = spectrum(3),如果数字越界,结果不确定,不会出错,但不能依赖得到的结果

  • 设置枚举量的值:

    • 可以显式地直接赋值:enum bits{one =1,two= 2,four =4,eight =8};

    • 指定的值必须事整数,也可以只显式地定义其中一些枚举量的值:enum bigstep{first,second =100,third};

    • 后面没被初始化的枚举量的值将比前面的大1

    • 可以创建多个值相同的枚举量:enum{zero,null= 0,one,numero_uno= 1};

  • 枚举的取值范围:

    • 上限:找到大于最大值的、最小的2的幂,减1:101的上限就是127(128-1)

    • 下限:同上限:-6的下限-7(-(8-1))

4.7 指针和自由存储空间

  • 显示地址时,cout使用16进制表示法,因为这是常用于描述内存的表示法,我在我vs试了下,不是十六进制表示法,是十进制,有些系统不会把两个变量存储在相邻的内存单元中,比如我的

  • 面向对象编程与传统的过程性编程的区别在于,OOP强调的是在运行阶段(而不是编译阶段)进行决策

  • *运算符被称为间接值(indirect value)或解除引用(dereferencing)运算符

  • 在C++中,int*是一种复合类型,是指向int的指针

  • C++中创建指针时,计算机只会分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存,为数据提供空间是一个独立的步骤。下面这个例子是危险的:

    1
    2
    
    long* fellow;
    *fellow = 23333;
    
  • 指针不是整型,C++不循序将数字值赋给指针,但可以使用强制类型转换,如:int* p = (int*)0xB80000000

  • int* pn = new it;pn指向了一个数据对象,指的是为数据项分配的内存块而不是OOP中的对象,虽然不如变量好用,但它使程序在管理内存方面有更大的控制权

  • double* p = new double;*p = 0.1;中,sizeof(p)为指针的大小,为4,sizeof(*p)为double的大小,为8

  • 变量存储在栈,而new从被称为堆或自由存储区的内存区域分配内存(后面第9章会详细介绍)

  • 内存耗尽,new会引发异常

  • delete指针不会删除指针p本身,而是释放其指向的内存

  • 不要尝试释放已经释放的内存,不能使用delete释放声明变量所获得的内存如:int jugs = 5;int* p=&jugs;不能delete这个p

  • delete是通过new的内存块的地址来删的,不是指针这个变量,多个指针可能指向同一个内存块,也就是说多个指针变量存储的地址相同,所以delete其中任何一个都会删除内存块

  • 对于大型数据,应使用new,这正是new的用武之地!

  • 动态数组:

    • 在编译时给数组分配内存被称为静态联编(static binding)。但使用new时,如果在运行阶段需要数组,则创建它,不需要就不创建,还可以在程序运行时选择长度,这杯称作静态联编(dynamic binding)。这种数组称为动态数组。

    • 使用new创建动态数组:

      1
      2
      
      int* psome = new int[10];
      delete []psome;
      
    • new和delete的规则

      • 不要使用delete来释放不是new分配的内存

      • 不要使用delete释放同一个内存块两次

      • 如果使用new[]为数组分配内存,则应使用delete[]来释放

      • 如果使用new为一个实体分配内存,则应使用delete(没有方括号)来释放

      • 对空指针应用delete是安全的。

4.8 指针、数组和指针算术

  • 指针和数组基本等价的原因在于指针算术(pointer arithmetic)和C++内部处理数组的方式。指针变量加1后,增加的量等于它指向的类型的字节数。比如指向double的指针+1后,则数值会+8(如果系统对double使用8个字节存储)

  • xYvpGjjpg

  • 自动存储、静态存储和动态存储

    1. 自动存储:

      • 在函数内部定义的常规变量使用自动存储空间被称为自动变量(automatic variable),这意味着它们在所属的函数被调用时自动产生,在该函数结束时消亡。

      • 自动变量通常存储在栈中。这意味着执行代码块时,其中的变量将依次加入到栈中,而在离开代码块时,将按相反的顺序释放这些变量

    2. 静态存储:

      • 静态存储是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字static:static double a = 0.1;

      • 自动存储和静态存储的关键在于:这些方法严格地限制了变量的寿命。变量可能存在于程序的整个生命周期(静态变量),也可能只是在特定函数被执行时存在(自动变量)

    3. 动态存储:

      • new和delete运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在C++中被称为自由存储空间(free store)或(heap)。该内存池同用于静态变量和自动变量的内存是分开的。

4.9 类型组合

  • 其实就是说数组、结构、指针可以组合使用

4.10 数组的替代品

  • 模板类vector和array是数组的替代品

    1. 模板类vector

      • 模板类vector类似于string类,也是一种动态数组。您可以在运行阶段设置vector对象的长度,可在末尾附加新数据,还可在中间插入新数据。基本上,它是使用new创建动态数组的替代品。实际上,vector类确实使用new和delete来管理内存,但这种工作是自动完成的。存放在自由存储区或堆
    2. 模板类array(C++11)

      • vector类的功能比数组强大,但付出的代价是效率稍低。如果您需要的是长度固定的数组,使用数组是更佳的选择,但代价是不那么方便和安全。有鉴于此,C+11新增了模板类array,它也位于名称空间std中。与数组一样,array对象的长度也是固定的,也使用栈(静态内存分配),而不是自由存储区,因此其效率与数组相同,但更方便,更安全

      • array<typename, n_elem> arr;:n_elem不能是变量,C++11中可以对vector和array使用列表初始化,C++98不能对vector使用

      • array可以将一个array对象赋给另一个array对象,但数组不行,只能逐元素复制

      • vector和array使用at()可以在运行期间捕获非法索引,程序默认将中断,代价就是运行时间更长

本文由作者按照 CC BY 4.0 进行授权

C++ Primer Plus-第3章 处理数据

C++ Primer Plus-第5章 循环和关系表达式