C++ Primer 0x03 学习笔记

时间:2022-11-25 19:59:12


???? C++ Primer 0x03 学习笔记

​更好的阅读体验(实时更新和修正)​


3.1 命名空间的 using 声明

  • 使用 using 声明可以免去命名空间前缀
  • 每个名字都需要独立的 using 声明
  • 头文件不应该包含 using 声明,如果头文件里用了 using 声明,那么引用他的文件也会用 using 声明

3.2 标准库类型 string

  • 使用等号是拷贝初始化,不用等号是直接初始化
  • ​cin​​​ >>读入 ​​string​​​ 对象,​​string​​ 对象会自动忽略开头的空白(空格符、换行符、制表符等)并从第一个真正的字符开始读起,知道遇到下一处空白
  • ​getline​​​会把换行符读入,但不存进 ​​string​​​。因为不包含换行符,所以我们有时候要手动加上换行操作符 ​​endl​​ 结束当前行并刷新
  • ​string​​​的 ​​size​​​ 函数返回 ​​string::size_type​​类型,是一个无符号整数,注意不要和有符号数混用。例如:如果n是个负的 int,那么 s.size() < n 永远为真,因为会转化为无符号整型自动取模
  • 字符串字面值对象不是 ​​string​​ 对象
  • ​string​​​ 对象可以相加,​​string​​​对象也可以和字符串字面值对象相加,但是必须保证每个 + 号两侧至少有一个对象是 ​​string​
  • 使用基于范围的​​for​​ 语句 处理每个字符,如果要改变字符串的字符,那么必须把循环变量定义成引用类型
  • 可以使用下标运算符访问某个位置的字符,如果超出范围会造成位置结果,而且由于 s.size() 是无符号整数,因此要下标的计算结果是否可能负数
  • 下标访问空字符串是非法的

3.3 标准库类型 vector

  • ​vector​​ 是类模板,而不是类型
  • 不存在引用的 ​​vector​​,存的得是对象
  • 可以只提供​​vector​​ 对象容纳的元素数量而不用略去初始值
  • 圆括号,可以说提供的值使用来构造对象的
  • 花括号,想列表初始化该对象爱那个,尽量把花括号里的值当成元素初始值的列表来处理,如果无法执行列表初始化,会考虑其他初始化方式。如果是花括号内的,又没法初始化,会考虑用这个值构造对象或者默认值初始化
  • 通常情况下,创建 ​​vector​​ 对象的时候直接指定容量并不会比动态添加快,甚至更慢
  • 范围 ​​for​​ 语句体内不应改变其遍历序列的大小
  • 要使用 ​​size_type​​​,需要首先指定它是由哪种类型定义的。​​vector​​对象的类型总是包含着元素的类型
  • 只有当元素的值可比较的时候,​​vector​​ 对象才能被比较,以字典顺序比较
  • ​vector​​​ 下标 0 到 ​​vec.size()-1​
  • 不能用下标形式添加元素,只能对确知已存在的元素执行下标操作

3.4 迭代器介绍

  • 迭代器有 ​​begin​​​ 和​​end​​​ 成员,​​end​​​ 成员指示的是尾元素的下一个元素。如果容器为空,​​begin​​​ 和​​end​​返回同一个迭代器,都是尾后迭代器
  • 迭代器可以进行解引用操作,尾后迭代器和非法迭代器不可以
  • 所有迭代器都支持 == 和 != 但大多数没定义 <,因此要养成迭代器和!=的习惯
  • ​iterator​​​ 和 ​​const_iterator​​​ 可以表示迭代器类型,​​begin​​​ 和 ​​end​​​ 返回的迭代器类型由对象是否是常量决定,​​cbegin​​​ 和 ​​cend​​​ 一定会得到 ​​const_iterator​
  • 箭头运算符把解引用和成员访问操作结合到一起
  • 但凡使用了迭代器的循环体,都不要向迭代器所属的容器添加元素
  • 两个迭代器相减得到两个迭代器的距离,类型是 ​​difference_type​​ 的带符号整数。两个迭代器不能相加,注意运算顺序。
  • 迭代器和下标互相转换其实就是​​begin()​​+下标

3.5 数组

3.5.1 定义和初始化数字

  • 如果不清楚元素的确切个数,请使用 ​​vector​
  • 数组维度应该在编译的时候知道,所以维度应该是个常量表达式
  • 和内置类型一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会令数组含有未定义的值
  • 定义数组的时候必须指定数组类型,不允许用 ​​auto​​ 关键字 由初始值的列表推断类型
  • 数组的元素应为对象,不存在引用的数组
  • 使用字符串字面值对字符数组初始话的时候,要记住结尾还有个空字符,所以大小至少要为显示字符数+1
  • 数组不允许拷贝和赋值,有的编译器扩展支持,但这不是标准
  • 要想理解数组声明的含义,最好的办法就是从数组的名字开始从内到外顺序阅读

3.5.2 访问数组元素

  • 在使用数组下标的时候通向将其定义为 ​​size_t​​类型(机器相关的无符号类型
  • 数组也可以使用 范围 for 循环语句
  • 下标应该>=0 而且<数组大小
  • 访问非法内存可能造成缓冲区溢出等问题

3.5.3 指针和数组

  • 在很多用到数组指针名字的地方,编译器会自动将其换成数组首元素的指针
  • 指针也是迭代器
  • 使用函数 ​​begin()​​​和​​end()​​​并把数组作为他们的参数可以获得类成员函数​​begin()​​​、​​end()​​类似的效果
  • 两指针相减是一种 ​​ptrdiff_t​​的带符号类型,两个空指针也允许相减,结果为0
  • 数组使用的下标运算是C++ 内置的,不是无符号类型,可以处理负数。这与 ​​vector​​​和 ​​string​​是不一样的,标准库类型限定使用的下标必须是无符号类型

3.5.4 C风格字符串

  • C风格字符串(字符串字面值)不是一种类型,只是一种约定俗成的写法。按此习惯书写的字符串存放在字符数组中,并以空字符结束,一般利用指针操作
  • 很多C风格字符串的函数不会检查字符串参数(例如不会看有没有空字符结束),调用的时候还要仔细思考目标字符串的大小
  • 对大多数应用来说,使用标准库 ​​string​​要比使用 C风格字符串更安全、更高效

3.5.5 与旧代码的接口

  • 尽量使用标准库类型而非数组
  • 任何出现字符串字面值的地方都可以用空字符结束的字符数组替代
  • 如果程序某处需要 C风格字符串,无法直接使用​​string​​​对象来代替它。例如不能用​​string​​​对象直接初始化指向字符的指针,不过可以用 ​​c_str​​​函数来完成(返回一个C风格的字符串),如果后续改变了​​string​​对象的值,之前返回的数组可能失效,所以要拷贝一份
  • ​vector​​对象允许使用数组初始化,只需要指定拷贝区域的首元素地址和尾元素地址即可
  • ​string​​​和​​vector​​ 往往可以兼容数组,但是反过来不行

3.6 多维数组

  • 严格来说,C++ 没有多为数组,平常说的多维数组其实是数组的数组
  • 可以使用下标运算来访问多维数组的元素。如果下标运算符数量和维度一样多,那么返回对应元素;如果下标运算符数量小于维度,那么返回内层数组
  • 可以使用范围 for 语句处理多维度数组,注意除了最内层的循环外,其他所有循环的控制变量都应该是引用类型的,避免数组被自动转成指针
  • 定义指向多维数组的名字时,也会将其转为数组首元素的指针,所以别忘了这个多为数组实际上是数组的数组
  • 可以使用类型别名简化多为数组的指针