C++ Primer 读书笔记 Day Two

第三章 字符串、向量和数组

  1. 头文件不应该包含命名空间的using声明,头文件的内容会拷贝到所有引用它的文件中去,若使用using声明可能会在别的文件中造成名字冲突

标准库类型string

  1. string s3("value");等同于string s3 = "value";s3是“value”除字面值最后空字符外的副本
  2. string s4(n,'c');s4初始化为连续n个字符c组成的串
  3. 使用等号(=)的是拷贝初始化;使用括号进行初始化的是直接初始化,当初始化要用到多个值时则一般使用直接初始化
  4. string对象的<,<=,>,>=操作是利用字典序进行比较的,且对字母的大小写敏感
  5. 补充字典序
    1)如果两个string对象长度不同,较短的每个字符都和较长的对应位置上相同,那么较短的小于较长的;
    2)如果两个string对象在某些对应的位置上不一致,则string对象比较的结果其实就是第一对相异字符的比较结果(优先判断)
  6. cin >> s1 >> s2 ;连续读入两个字符串
  7. 读取未知数量的string对象,一般使用while(cin>>woild)如果希望最终得到的字符串中保留输入时的空白符,则需要使用getline函数代替cinwhile(getline(cin,string))cin内容读内容,直到遇到换行符(换行符也读入),然后存到string中(换行符不存,因此输出的时候若需要换行符则要补换行符),getline也是返回其流参数,因此while检查的还是流的状态
  8. stringsize函数返回的是一个string::size_type类型,这是一个无符整型数,在表达式中要避免无符号数和带符号数的混用。如果拿size_typeint比较,若int为负值,那么负值往往会自动转换成一个比较大的无符号值
  9. string对象的相加操作,标准库允许字符字面值和字符串字面值转换成string对象进行相加,但是必须保证每个加法运算符(+)的两侧的运算对象至少有一个是string对象,字面值直接相加是不允许的
  10. 处理每个字符使用范围for语句,如果要改变string对象中字符的值,必须把循环变量定义成引用类型
  11. 对每个字符的操作可以使用标准库函数,头文件包含cctype,详细查询其包含的函数
  12. 在访问指定字符前一定要先检查字符串对象是否为空

标准库类型vector

  1. vector是一个类模板,编译器根据模板创建类或者函数的过程称为实例化,由于引用不是对象,因此不存在引用的vector

  2. vector对象的定义和初始化

    vector<T> v4(n);//v4包含了n个重复地执行了值初始化的对象
    vector<T> v5{a,b,c..};//列表初始化
    vector<T> v6 = {a,b,c..};//等价于上一句
    vector<T> vi = 10;//必须使用直接初始化的形式指定向量的大小,即使用圆括号
    
  3. 如果初始化用的是圆括号,则提供的值是用来构造(construct)vector对象的

  4. 如果使用的是花括号,则我们想要的是列表初始化,初始化过程尽可能把花括号内的值当成是元素初始值的列表来处理,当提供的值不能用来初始化时,就要考虑用这样的值来进行构造vector对象

    vector<string> v7{10};//v7有10个默认初始化的元素
    vector<string> v8{10,"hi"};//v8有10个值为“hi”的元素
    
  5. 容器相关操作在第九章

迭代器

  1. 很多容器都不支持下标运算符,由 迭代器(iterator)来实现同样的目的
  2. end成员返回的尾后迭代器是个标记,表示已经处理完了容器中的所有元素,如果容器为空,则beginend返回的是用一个迭代器,都是尾后迭代器
  3. 迭代器运算符操作
    *iter          //返回迭代器所指元素的引用	
    iter->mem      //解引用iter并获取该元素名为mem的成员,等价于(*iter).mem
    ++iter         //令iter指向容器的下一个元素
    --iter		   //令iter指向容器的上一个元素
    
  4. 迭代器的递增是将迭代器“向前(向尾部)移动一个位置”
  5. 标准库类型使用iteratorconst_iterator来表示迭代器的类型string::const_iterator it;
  6. C++11定义了cbegincend来得到const_iterator类型的迭代器
  7. 谨记,但凡使用了迭代器的循环体(例如范围for),都不要向迭代器所属的容器添加元素
  8. 两个迭代器相减的结果是迭代器之间的距离,类型名是difference_type的带符号整型数,可正可负

数组

  1. 数组的大小是确定不变的,维度必须是个常量表达式,因此不能随意向数组中添加元素

  2. 维度比提供的初始值数量大,那么用提供的初始值初始化靠前的元素,剩下元素默认初始化,反之报错

  3. 当没有定义数组的维度时,当初始化结束后编译器会根据元素个数推算出维度

  4. 字符数组的特殊性,即当用字符串字面值初始化字符数组时要注意,字符串字面值的结尾处还有一个空字符,也会被拷贝到字符数组中去

    ```c
    	char a1[] = {'c','+'};//列表初始化,不会添加空字符
    	char a2[] = "C++";    //自动添加表示字符串结束的空字符
    	const char a3[6] = "Daniel";//错误,没有空间存放空字符!
    ```
    
  5. 数组不允许拷贝和赋值,不能将数组的内容拷贝给其他数组作为其初始化值,也不能用数组为其他数组赋值

  6. 理解复杂的数组声明:
    cpp int *ptrs[10]; //ptrs是含有10个整型指针的数组 int &refs[10] = /* ? */ //错误:不存在引用的数组 int (*Parray)[10] = &arr;//Parray指向一个含有10个整数的数组 int (&arrRef)[10] = arr; //arrRef引用一个含有10个整数的数组
    由于数组的维度是紧跟着被声明的名字的,所以就数组而言,由内向外的阅读要比从右向左的好多了;由内向外,再右再左

  7. 数组的下标数据类型是size_t,这是一种机器相关的无符号类型

  8. 遍历数组中的每个元素同容器一样也是使用 范围for 语句

  9. 编译器会把数组名替换为指向数组首元素的指针;但是当使用decltype关键字时,上述转换不会发生,即当 ia 为包含十个元素的数组名时:decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9};//ia3是一个包含是个元素的数组,而不是一个整型指针

  10. 标准库函数beginend,这两个函数同容器中的两个同名成员函数功能类似,但是由于数组不是类类型,因此没有成员函数,只能将数组当作这两个函数的参数来使用:
    int *beg = begin(ia);//beg为指向ia首元素的指针
    需要注意的是,尾后指针不能执行解引用和递增操作

  11. 两个指针相减同两个迭代器相减差不多,结果类型是一种名为ptrdiff_t的标准库类型,由于差值可正可负,因此也是带符号类型

  12. 指针使用下标为带符号类型,与vector和string不同:

    int *p = &ia[2]; //p指向索引为2的元素
    int j = p[1];    //p[1]等价于*(p+1),就是ia[3]表示的那个元素
    int k = p[-2];   //p[-2]是ia[0]表示的那个元素
    
  13. 多维数组在C++中即是数组的数组,把第一个维度称为行,第二个维度称为列

  14. 多维数组的初始化

    int ia[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};
    int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};//同上,内嵌花括号不是必须的
    int ia[3][4] = {{0},{4},{8}};//显示地初始化每行的首元素,其他元素默认初始化
    int ia[3][4] = {0,3,6,9};//显示地初始化第一行,其他未列出元素默认初始化
    
  15. 使用范围for语句处理多维数组

    size_t  cnt = 0;
    for(auto &row : ia)
    	for(auto &col : row){
    		col = cnt;
    		++cnt;
    	}
    

C风格字符串

  1. 按C风格习惯书写的字符串存放再字符数组中并以空字符结束’\0’
  2. cstring是C语言头文件string.h的C++版本,C风格字符串函数
    strlen(p) //返回p的长度,空字符不记录在内
    strcmp(p1,p2) //比较p1和p2的相等性,==返回0;>返回正数;<返回负值
    strcat(p1,p2)//将p2附加到p1后返回p1
    strcpy(p1,p2)//将p2拷贝给p1,返回p1
  3. 传入以上这些函数的指针必须指向以空字符作为结束的数组,尤其初始化列表的那些字符数组,因此使用标准库string要安全的多
  4. 不能用string对象直接初始化指向字符的指针,string专门提供一个c_str的成员函数来返回C风格的字符串const char *str = s.c_str();
  5. 同理,想要使用数组初始化vector对象,可以拷贝区域的首元素地址和尾后地址
    vector<int > ivec(begin(arr),end(arr));

第四章 表达式

  1. 当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)
  2. 使用关键字decltype的时候,如果表达式的求值结果是左值,则作用于这个表达式的结果就是一个引用类型,举例来说,如果p的类型是一个int *,因为解引用运算符生成左值,所以decltype(*p)的结果就是int&,另一方面,因为取地址运算符生成右值,所以 decltype(&p) 的结果是int **,也就是结果是一个指向整型指针的指针
  3. 复合表达式中要考虑优先级和结合律,表达式中的括号无视上述规则,优先级规定了运算对象的组合方式,但是运算对象的求值顺序与优先级和结合律无关
  4. 当表达式中有多个函数时,且这些函数是无关函数时,它们的调用顺序不受限制,反之,当其中某几个函数影响同一个对象,则表达式错误,产生未定义的行为
  5. 当拿不准时,使用括号来强制让表达式符合逻辑关系

运算符

  1. 算术运算符都满足左结合律

  2. 当算术运算符运算时发生溢出时,有些计算机会发生“环绕”,符号位本来是0,但是由于溢出被改成了1,于是结果变成了一个负值,有时候也会导致程序的崩溃

  3. 除法运算中,C++11新标准规定运算对象带符号进行除,并且结果一律向0取整(即直接切除小数部分)

  4. 总的来说,(- m) / n和m / (- n)都等于- (m / n),m % (- n)等于m % n,(- m) % n等于- (m % n)

  5. 逻辑与 和 逻辑或 运算符都是先求左侧运算对象的值,再求右侧,当且仅当左侧运算对象无法确定表达式结果时才会计算右侧对象得到值,称为短路求值

  6. 赋值运算符满足的是右结合律,且优先级较低

  7. 后置递增运算符的优先级高于解引用的运算符,因此*p++等价于*(p++),可用于输出p开始时指向的元素,并将指针p向前移动一个位置

  8. 大多数运算符对于运算对象的求值顺序没有规定,容易导致不同运算顺序产生不同的结果,尤其要提防递增递减运算符也会改变对象的值。

  9. 条件运算符(cond)?expr1:expr2; 可以进行在冒号后进行嵌套,最多不要超过三层

  10. 由于条件运算符的优先级很低,所以在输出表达式中使用条件运算符最好用括号将其括起来

  11. 强烈建议仅将位运算符用于处理无符号类型

  12. 移位运算符<<、>>右侧的运算对象一定不能为负,而且值必须严格小于结果的位数,否则就会产生未定义的行为,二进制数移位当移出边界之外的位就被舍弃掉

  13. 运算对象如果是“小整型”,那么它的值就会被自动提升到“大整型”数

  14. 位求反运算符(~)按每一位取反,如果运算对象是类似 char 这样的小整型,则先会被提升到 int 类型,由8位提升到32位,高位补0后再对每一位取反

  15. 移位运算符(又叫IO运算符)满足左结合律cout << A << B << endl;

  16. sizeof(type)等价于sizeof type

  17. 对指针执行sizeof运算得到指针本身所占空间的大小,对解引用指针执行sizeof运算得到指针所指向的对象所占空间的大小,指针不需有效

  18. 对数组执行sizeof运算得到整个数组所占空间的大小,sizeof( ia ) / sizeof( *a ) //返回数组ia中的元素数量

  19. string和vector对象执行 sizeof 运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间

  20. 对于逗号运算符来说,首先对左侧的表达式求值,然后将求值的结果丢弃,逗号运算符真正的结果是右侧的表达式的值,如果右侧运算对象是左值,那么最终结果也是一个左值

    for(vector<int>::size_type ix = 0;
    		ix != tvec.size(); ++ix, --cnt)
    		{  ivec[ix]=cnt;}
    

    这个循环中递增ix、递减cnt,每次循环迭代ix和cnt相应改变,但是ix满足条件,就把当前元素设成cnt的当前值

  21. 算术转换时隐式转换中的一种,进行算术转换的时候,小整型,像char之类的先提升成大整型,提升后如果所有运算对象都符合同一种类型,就无需再进行转换,如果不同则再进一步提升,如果有带符号和不带符号的,要根据相对大小进行类型转换,如果无符号类型不小于带符号的,则带符号的转换成无符号的,相反,则无符号的转换成带符号的

类型转换

  1. static_cast是一种具有明确定义的显示转换,只要转换类型不包括底层const,通过将一个运算对象强制转换成double类型就能使表达式执行浮点数除法:
    cpp double slope = static_cast<double>(j) / i;//j和i本身是int类型
  2. const_cast只能改变运算对象的底层const,使允许一个并非指向const变量的指针去指向一个const变量,const_cast常用在有函数重载的上下文中
    const char *pc;
    char *p = const_cast<char *>(pc);//正确,但是通过p写值是未定义的行为
    ``
    
  3. 运算符优先级表P147

猜你喜欢

转载自blog.csdn.net/qq_41423750/article/details/102986680