《C++ Primer》笔记 第4章 表达式

  1. C++的表达式要不然是右值(right-value or read-value),要不然就是左值(left-value or location-value)。
  2. 当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。
  3. 需要右值的地方可以用左值来代替,但是不能把右值当成左值(也就是位置)使用。当一个左值被当成右值使用时,实际使用的是它的内容(值)。到目前为止,已经有几种我们熟悉的运算符是要用到左值的:
    • 赋值运算符需要一个(非常量)左值作为其左侧运算对象,得到的结果也仍然是一个左值。
    • 取地址符作用于一个左值运算对象,返回一个指向该运算对象的指针,这个指针是一个右值。
    • 内置解引用运算符、string和vector的下标运算符的求值结果都是左值。
    • 内置类型和迭代器的递增递减运算符作用于左值运算对象,其前置版本所得的结果也是左值。
  4. 如果表达式的求值结果是左值,decltype作用于该表达式(不是变量)得到一个引用类型。
  5. 如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象。
  6. 如果m%n不等于0,则它的符号和m相同。
  7. 逻辑与(&&)和逻辑或(||)的短路求值。
  8. 赋值运算符:如果左侧运算对象是内置类型,那么初始值列表最多只能包含一个值,而且该值即使转换的话其所占空间也不应该大于目标类型的空间。对类类型来说,赋值运算的细节由类本身决定。无论左侧运算对象的类型是什么,初始值列表都可以为空。此时,编译器创建一个值初始化的临时量并将其赋给左侧运算对象。(例:int a[5] = {};
      k = {3.14}; // 错误:窄化转换(换成圆括号正确)
      vector<int> vi; // 初始值为空
      vi = {0, 1, 2, 3, 4, 5}; // vi现在含有6个元素了,0到5
    
  9. 箭头运算符作用于一个指针类型的运算对象,结果是一个左值。点运算符分成两种情况:如果成员所属的对象是左值,那么结果是左值;反之,如果成员所属的对象是右值,那么结果是右值。
  10. 随着条件运算(?:)嵌套层数的增加,代码的可读性急剧下降。因此,条件运算的嵌套最好别超过两到三层。
  11. 条件运算符的优先级非常低,因此当一条长表达式中嵌套了条件运算子表达式时,通常需要在它两端加上括号。
  12. 关于符号位如何处理没有明确的规定,所以强烈建议仅将位运算符用于处理无符号类型。
  13. 使用位运算:
      quiz1 |= 1UL << 27; // 表示学生27通过了测验
      quiz1 &= ~(1UL << 27); // 学生27没有通过测验
      bool status = quiz1 & (1UL << 27); // 学生27是否通过了测验
    
  14. sizeof运算符的运算对象有两种形式sizeof (type)sizeof expr。在第二种形式中,sizeof返回的是表达式结果类型的大小。与众不同的一点是,sizeof并不实际计算其运算对象的值(有点像decltype)。例:sizeof data.revenue;等价于sizeof Sales_data::revenue;
  15. sizeof不需要真的解引用指针也能知道它所指对象的类型。
  16. sizeof运算符的结果部分地依赖于其作用的类型:
    • 对char或者类型为char的表达式执行sizeof运算,结果得1。
    • 对引用类型执行sizeof运算得到被引用对象所占空间的大小。
    • 对指针执行sizeof运算得到指针本身多占空间的大小。
    • 对解引用指针执行sizeof运算得到指针指向的对象所占空间的大小,指针不需有效(因为不实际解引用)。
    • 对数组执行sizeof运算得到整个数组所占空间的大小(这里不再是数组首地址,和decltype类似),等价于对数组中所有的元素各执行一次sizeof运算并将所得结果求和。注意,sizeof运算不会把数组转换成指针来处理。
    • 对string对象或vector对象执行sizeof运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间。
  17. 因为执行sizeof运算能得到整个数组的大小,所以可以用数组的大小除以单个元素的大小得到数组中元素的个数
  18. 因为sizeof的返回值是一个常量表达式,所以我们可以用sizeof的结果声明数组的维度。
  19. 当表达式中既有浮点类型也有整数类型时,整数值将转换成相应的浮点类型。
  20. 整型提升负责把小整数类型转换成较大的整数类型。对于bool、char、signed char、unsigned char、short和unsigned short等类型来说,只要它们所有可能的值都能存在int里,他们就会提升成int类型(直接一步提升至int,没有中间类型);否则,提升成unsigned int类型。
  21. 较大的char类型(wchar_t、char16_t、char32_t)提升成int、unsigned int、long、unsigned long、long long和unsigned long long中最小的一种类型,前提是转换后的类型要能容纳原类型所有可能的值。
  22. 如果一个运算对象是无符号类型、另外一个运算对象是带符号类型,而且其中的无符号类型不小于带符号类型,那么带符号的运算对象转换成无符号的。如果带符号类型大于无符号类型,此时转换的结果依赖于机器。如果无符号类型的所有值都能存在该带符号类型中,则无符号类型的运算对象转换成带符号类型。如果不能,那么带符号类型的运算对象转换成无符号类型。
  23. 当数组被用作decltype关键字的参数,或者作为取地址符(&)、sizeof及typeid等运算符的运算对象时,数组转换成指针的转换不会发生。
  24. 指针的转换:C++还规定了几种其他的指针转换方式,包括常量整数值0或者字面值nullptr能转换成任意指针类型;指向任意非常量的指针能转换成void*;指向任意对象的指针能转换成const void*
  25. 允许将指向非常量类型的指针转换成指向相应的常量类型的指针,对于引用也是这样。(即非常量转换成常量)
  26. 类类型定义的转换:一处是在需要标准库string类型的地方使用C风格字符串,例:string s = "a value";;另一处是在条件部分读入istream,例:while (cin >> s)
  27. static_cast:任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。
      double slope = static_cast<double>(j) / i; // 进行强制类型转换以便执行浮点数除法
      void* p = &d; // 正确:任何非常量对象的地址都能存入void*
      double *dp = static_cast<double*>(p); // 正确:将void*转换回初始的指针类型
    
  28. const_cast:const_cast只能改变运算对象的底层const。
      const char *pc;
      char *p = const_cast<char*>(pc); // 正确:但是通过p写值是未定义的行为
    
      const char *cp;
      char *q = static_cast<char*>(cp); // 错误:static_cast不能转换掉const性质
      static_cast<string>(cp); // 正确:字符串字面值转换成string类型
      const_cast<string>(cp); // 错误:const_cast只改变常量属性
    
      // const_cast常常用于有函数重载的上下文中。
    
  29. reinterpret_cast:reinterpret_cast通常为运算对象的位模式提供较低层次上的重新解释。reinterpret_cast本质上依赖于机器,想要安全地使用reinterpret_cast必须对涉及的类型和编译器实现转换的过程都非常了解。
      int *ip;
      char *pc = reinterpret_cast<char*>(ip);
      // 我们必须牢记pc所指的真实对象是一个int而非字符,如果把pc当成普通的字符指针使用就可能在运行时发生错误。例如:string str(pc);
    
  30. 旧式的强制类型转换:type (expr);(type) expr;
  31. ++运算符:前置递增运算符(++i)得到一个左值,它给运算符加1并得到运算对象改变后的值。后置递增运算符(i++)得到一个右值,它给运算符加1并得到运算对象原始的、未改变的值的副本
(0)

相关推荐

  • 【网络编程】C语言程序设计教程

    [网络编程]C语言程序设计教程 C语言程序设计视频教程:本套视频教程由曾怡副教授讲解.是难得的C语言学习视频教程.全程共30讲,每讲45分钟左右,讲课内容如下:1第一章 C语言概述2第二章 程序的灵魂 ...

  • java学习——9运算符与表达式(四)

    本篇接java学习--8运算符与表达式(三) (6)位运算符 位运算是指对整数按二进制的位进行运算. 位运算用于整数或字符类型. 有7个:~(非).&(与).|(或).^(异或).<&l ...

  • 《C++ Primer》笔记 第10章 泛型算法

    迭代器令算法不依赖于容器,但算法依赖于元素类型的操作. 算法永远不会执行容器的操作.算法永远不会改变底层容器的大小. accumulate定义在头文件numeric中,接受三个参数,前两个指出需要求和 ...

  • 《C++ Primer》笔记 第9章 顺序容器

    顺序容器类型 类型 解释 vector 可变大小数组.支持快速随机访问.在尾部之外的位置插入或删除元素可能很慢 deque 双端队列.支持快速随机访问.在头尾位置插入.删除速度很快 list 双向链表 ...

  • 《C++ Primer》笔记 第11章 关联容器

    关联容器类型 解释 按关键字有序保存元素 -- map 关联数组:保存关键字-值对 set 关键字即值,即只保存关键字的容器 multimap 关键字可重复出现的map multiset 关键字可重复 ...

  • 《C++ Primer》笔记 第8章 IO库

    iostream定义了用于读写流的基本类型,fstream定义了读写命名文件的类型,sstream定义了读写内存string对象的类型. 标准库使我们能忽略这些不同类型的流之间的差异,这是通过继承机制 ...

  • 《C++ Primer》笔记 第5章 语句

    空块的作用等价于空语句. case标签必须是整型常量表达式,default也是一种特殊的case标签. 标签不应该孤零零地出现,它后面必须跟上一条语句或者另外一个case标签. 如果在某处一个带有初值 ...

  • 《C++ Primer》笔记 第3章 字符串、向量和数组

    位于头文件的代码一般来说不应该使用using声明. 如果使用等号(=)初始化一个变量,实际上执行的是拷贝初始化,编译器把等号右侧的初始值拷贝到新创建的对象中去.与之相反,如果不使用等号,则执行的是直接 ...

  • 《C++ Primer》笔记 第2章 变量和基本类型

    如果你的数值超过了int表示范围,选用long long 如果你需要使用一个不大的整数,那么明确指定它的类型是signed char或者unsigned char 执行浮点数运算选用double 当一 ...

  • 《C++ Primer》笔记 第1章 开始

    输出运算符<< 的计算结果就是其左侧运算对象 std::endl 结束当前行,并将与设备关联的缓冲区中的内容刷到设备中. 程序员常常在调试时添加打印语句.这类语句应该保证"一直& ...

  • 2021高考数学热门考点笔记(6-11章)

    2021高考数学热门考点笔记(6-11章)