表达式的意义:
1)定义了表达式计算过程(运算符的优先级 和结合律 运算对象的求值顺序)
2)指出对环境(可以把环境看作当时可用的所有变量)是否有影响。即有否有副作用。
左值和右值
当一个对象被用作右值的时候,用的是对象的内容;当对象被用作时左值的时候,用的是对象的身份(在内存中的位置)
- 赋值运算符需要一个非常量左值作为其左侧运算对象,返回结果也是一个左值。
- 取地址符作用于左值运算对象,返回指向该运算对象的指针,该指针是一个右值。
- 内置解引用运算符、下标运算符、迭代器解引用运算符、string和vector的下标运算符都返回左值。
- 内置类型和迭代器的递增递减运算符作用于左值运算对象。前置版本返回左值,后置版本返回右值。
如果decltype作用于一个求值结果是左值的表达式,会得到引用类型。
优先级和结合律
复合表达式(compound expression)指含有两个或多个运算符的表达式。优先级与结合律决定了运算对象的组合方式。
括号无视优先级与结合律,表达式中括号括起来的部分被当成一个单元来求值,然后再与其他部分一起按照优先级组合。
求值顺序
对于那些没有指定执行顺序的运算符来说,如果表达式指向并修改同一个对象,将会引发错误并产生未定义行为
// << 运算符没有明确规定何时对运算对象求值
int i = 0;
cout<< i << " "<< ++i <<endl; // 未定义的
有4中运算符明确规定了运算对象的求值顺序:&& 、||、?:、,(只有当左侧运算对想法的值运行完了之后才运行右侧的).
运算对象的求值顺序与结合律和优先级无关
如:f() + g()*h() +j()
优先级规定: g() 返回值和f() 返回值相乘
结合律:f() 的返回值先与g()和h() 的乘积相加
对于函数的调用顺序没有明确的规定,如果其中某几个函数影响同一对象,则它会产生未定义错误
处理复合表达式建议:
- 拿不准时用括号
- 如果改变了一个运算对象的值,在表达式的其他地方就不要在使用这个运算对象(当改变运算对象的子表达式本身就是另外一个子表达式的运算对象时,是可以的 如:*++iter
算术运算符(左结合律)
在除法运算中,C++语言的早期版本允许结果为负数的商向上或向下取整,C++11新标准则规定商一律向0取整(即直接去除小数部分)。
逻辑和关系运算符
关系运算符作用于算术类型和指针类型,逻辑运算符作用于任意能转换成布尔值的类型。逻辑运算符和关系运算符的返回值都是布尔类型(除了0为false,其他值都是true)。
赋值运算符(右结合律)
赋值运算符=的左侧运算对象必须是一个可修改的左值。
C++11新标准允许使用花括号括起来的初始值列表作为赋值语句的右侧运算对象。
递增表达式和递减表达式
前置版本将对象本身作为左值返回,后置版本将对象原始值的副本作为右值返回。
除非必要,否则不用递增递减运算符的后置版本
前置版本的递增运算符避免了不必要的工作,它把值加1后直接返回改变了的运算对象.
后置版本需要将元素值存储下来以便返回这个未修改的内容,如果不需要修改前的值,那么后置版本的操作就是一种浪费。
成员访问运算符
首先介绍下点运算符,用于获取类对象的一个成员。点运算符与箭头运算符之间的关系:
ptr—>mem 与 (*ptr).mem 等价。
条件运算符
cond ? expr1 : expr2;
只有当条件运算符的两个表达式都是左值或者能转换成同一种左值类型时,运算的结果才是左值,否则运算的结果就是右值。
位运算符(左结合律)
在位运算中符号位如何处理并没有明确的规定**,所以建议仅将位运算符用于无符号类型的处理。**
左移运算符<<在运算对象右侧插入值为0的二进制位。右移运算符>>的行为依赖于其左侧运算对象的类型:如果该运算对象是无符号类型,在其左侧插入值为0的二进制位;如果是带符号类型,在其左侧插入符号位的副本或者值为0的二进制位,如何选择视具体环境而定。
sizeof运算符
sizeof运算符返回一个表达式或一个类型名字所占的字节数,返回值是size_t类型。
在sizeof的运算对象中解引用一个无效指针仍然是一种安全的行为,因为指针实际上并没有被真正使用。
sizeof运算符的结果部分依赖于其作用的类型:
- 对char或者类型为char的表达式执行sizeof运算,返回值为1。
- 对引用类型执行sizeof运算得到被引用对象所占空间的大小。
- 对指针执行sizeof运算得到指针本身所占空间的大小。
- 对解引用指针执行sizeof运算得到指针指向的对象所占空间的大小,指针不需要有效。
- 对数组执行sizeof运算得到整个数组所占空间的大小。
- 对string或vector对象执行sizeof运算只返回该类型固定部分的大小,不会计算对象中元素所占空间的大小。