第4章 表达式

第4章 表达式

C++ Primer 学习记录

昨天写博客时用的是博客园自带的 MarkDown编辑器,一点儿都不好用,插入代码块和段落缩进很难搞,传统的 MarkDown语法说四个空格或者一个 Tab就可以缩进,结果博客园自带的编辑器里这样做居然不行。再搜了很多个 MarkDown编辑器后,决定还是使用小书匠,因为这个好像可以直接把文章发布到博客园上。这就很方便了。话不多说,进入正题。 1.运算符的三个关键点:优先级、结合律、求值顺序。 2.在重载运算符时,运算对象的类型和返回值的类型可以改变,但运算对象的个数、运算符的优先级和结合律都是无法改变的。 3.decltype作用于表达式时,当表达式的求值结果是左值时,得到的是引用类型;当求值结果是右值时,得到的是值类型。

	int a = 5;
	int *p = &a;
	decltype(*p) p1;  // p1是 int &
	decltype(&p) p2;  // p2是 int **

4.C++语言对于大多数的运算符并没有规定求值顺序,对于这些运算符,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为。

	int i = 0;
	cout << i << " " << ++i << endl;    // 未定义

编译器可能先求 ++i的值再求 i的值,也可能先求 i的值再求 i的值。此表达式的行为不可预知。有 4种运算符规定了它们的求值顺序,分别是 &&、||、条件(?:)和逗号(,)。 5.对于整数的除法和取余运算中,C11新标准中规定商一律向 0整除(即直接切除小数部分)。

	21 % -5;       /* 结果是 1 */       21 / -5;       /* 结果是 -4 */ 
	-21 % -5;      /* 结果是 -1 */    -21 / -5;       /* 结果是  4 */ 

6.对于二元运算符,算术>关系>逻辑>赋值。所以在条件语句中,赋值部分通常加上括号。 if ((i = get_value()) != 42) 7.对于递增/递减运算符,优先使用前置版本,因为后置版本需要在修改前将原始值存储下来,效率更低。 8.条件运算符的优先级非常低,在输出表达式中使用条件运算符时要在两端加上括号。

	cout << ((grade < 60) ? "fail" : "pass");    // 输出 pass或 fail
	cout << (grade < 60) ? "fail" : "pass";      // 输出 1或者 0后根据 cout的值输出 pass或 false

9.如果运算对象是带符号的且它的值为负,那么位运算符如何处理运算对象的“符号位”依赖于机器。而且,此时的左移操作可能会改变符号位的值,是一种未定义行为。因此建议仅将位运算符用于处理无符号类型。 10.位异或运算符(^),两个运算对象相同,结果为 0,反之为 1。真值表如下

0

1

0

0

1

1

1

0

11.sizeof,并不实际计算其运算对象的值。因此,在作用于解引用的指针时,即使该指针是一个未初始化的指针也不会有影响,它返回的是所值类型的空间大小。   对 char或者类型为 char的表达式执行 sizeof运算,结果得 1。   对引用类型执行 sizeof运算,得到被引用对象所占空间的大小。   对指针执行 sizeof运算,得到指针本身所占空间的大小。   对解引用指针执行 sizeof运算,得到指针所指的对象所占空间的大小,指针不需有效。   对数组执行 sizeof运算得到整个数组所占空间的大小,并不会将数组转换为指针。   对 string或 vector对象执行 sizeof运算,只返回该类型固定部分的大小,和里面存放了多少数据无关。 12.对无符号类型和带符号类型进行运算,其结果比较复杂,也依赖于具体机器,所以应该尽量避免无符号类型和带符号类型的运算! 13.类型转换

  1. 隐式类型转换 1. 类型提升 比如 bool、char、signed char、unsigned char、short和 unsigned short可能会被提升为 int或 unsigned int类型。 2. 算术类型转换,将运算对象转换成最宽的类型。比如表达式中既有浮点型也有整型数据时,整数会转换成相应的浮点型。 3. 数组转换成指针。大多数情况下,数组都能转换成指向首元素的指针。但是当数组作为 decltype的参数、取地址符(&)、sizeof和 typeid的运算对象时,数组并不会转换为指针。而在进行模板实参推断时,如果函数形参不是引用类型,则可以将数组或函数类型转换为普通的指针;相反则不可以。 4. 指针的转换。     1. 常量整数值 0或字面值 nullptr能转换成任意指针类型。     2. 指向任意非常量的指针能转换成 void*。     3. 指向任意对象的指针能转换成 const void*。     4. 派生类指针或引用能转换成基类的指针或引用。 5. 算术或指针类型向布尔类型的转换。 6. 指向非常量的指针或引用转换成相应的常量类型的指针或引用。 7. 类类型定义的转换。对于未使用 explicit修饰的构造函数或重载了类型转换运算符的类类型,编译器可以自动执行一次类类型的转换。
  2. 显式类型转换
    1. static_cast。任何具有明确定义的类型转换,只要不包括底层 const,都可以使用 static_cast。当需要将较大的算术类型赋值给较小的算术类型时,static_cast非常有用,它可以关闭编译器给出的警告信息。另外还可以找回存在于 void*指针中的值。 double d = 3.14; int i = static_cast<int>(d); void *p = &d; double *pd = static_cast<double*>(p); // 将 void*转换回初始的指针类型
    2. const_cast。只能改变运算对象的底层 const,将常量对象转换成非常量对象。
    3. reinterpret_cast。对运算对象提供较低层次上的重新解释,使用起来非常危险,一般不建议使用。
    4. dynamic_cast。运行时类型识别,用于将基类指针或引用安全地转换为派生类的指针或引用。 14.旧式的类型转换从表现形式上不如强制转换那么明显,一旦出现问题,追踪起来并不容易。所以新的 C++程序,推荐使用显式类型转换。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java学习123

40个你可能不知道的Python的特点和技巧

281100
来自专栏乐百川的学习频道

Golang学习笔记 方法和接口

在编程语言中,方法和函数的概念需要搞清楚。函数指的是一个封装的代码块,我们可以直接调用它,并返回结果。而方法其实也是一种函数,只不过方法需要和某个对象绑定。Go...

24580
来自专栏Golang语言社区

Golang方法和接口

在编程语言中,方法和函数的概念需要搞清楚。函数指的是一个封装的代码块,我们可以直接调用它,并返回结果。而方法其实也是一种函数,只不过方法需要和某个对象绑定。Go...

35050
来自专栏SeanCheney的专栏

Python题目

简述函数式编程 在函数式编程中,函数是基本单位,变量只是一个名称,而不是一个存储单元。除了匿名函数外,Python还使用fliter(),map(),red...

567160
来自专栏Code_iOS

Objective-c 知识总结 -- 继承

观察发现,它们属性和方法声明是相同的,都有 填充色(fillcolor)、尺寸+位置(bounds)、绘制方法;

13310
来自专栏http://www.cnblogs.com

生成器&迭代器

一.生成器 在介绍生成器表达式之前,先看下列表表达式: 1 >>> l = [i for i in range(50) if i % 2] #生成...

348100
来自专栏web前端-

JavaScript基础概述

    1.常量: 常量就是在程序运行过程中,不会发生变化的量,常量通常用来表示固定不变的量,比如圆周率,万有引力常量

8120
来自专栏前端杂谈

es6之块级作用域

282110
来自专栏与神兽党一起成长

前序遍历树

代码来自:pickle and cPickle – Python object serialization 首先树的结构,如图

17320
来自专栏我爱编程

Day18内建模块collections&base64collectionsbase64

collections collections是Python内建的一个集合模块,提供了许多有用的集合类。 namedtuple >>> from collect...

42880

扫码关注云+社区

领取腾讯云代金券