基础
左值和右值
C++的表达式要不然是右值( rvalue) ,要不然就是左值(lvalue) 。这两个名词是从C语言继承过来的,原本是为了帮助记忆:左值可以位于赋值语句的左侧,右值则不能。 :::info
- 当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。
- 一个重要的原则是在需要右值的地方可以用左值来代替,但是不能把右值当成左值(也就是位置)使用。
- 当一个左值被当成右值使用时,实际使用的是它的内容(值)。到目前为止,已经有几种我们熟悉的运算符是要用到左值的。(赋值运算符,取地址运算符,迭代器递增递减运算符)
:::
求值顺序
优先级规定了运算对象的组合方式,但是没有说明运算对象按照什么顺序求值。在大多数情况下,不会明确指定求值的顺序。
对于那些没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为
int i=0;
cout<< i<<" "<< ++i<<endl//未定义顺序,取决于编译器or报错
有4 种运算符明确规定了运算对象的求值顺序。 :::info
- 逻辑与(
&&
)运算符:先求左侧运算对象的值,只有当左侧运算对象的值为真时才继续求右侧运算对象的值。 - 逻辑或(
||
) 运算符: - 条件(
? :
)运算符: - 逗号
,
: ::: :::tips 求值顺序和优先级结合律没有关系!
逻辑与逻辑或执行短路求值,即若是左边为真,这不进行右边的计算;
相等运算符则会计算等号两边的值 :::
优先级确定了先计算f()+g()*h()+j();
g()*h()
,但是这些函数求值的顺序是不确定的;若是这些函数是相关的,那么这条语句是错误的,因为无法确定函数调用的顺序。
算术运算符
一元运算符的优先级最高,接下来是乘法和除法,优先级最低的是加法和减法。优先级高的运算符比优先级低的运算符组合得更紧密。上面的所有运算符都满足左结合律,意味着当优先级相同时按照从左向右的顺序进行组合。
:::info
溢出:
超过当前类型所表示的范围
:::
赋值运算
赋值运算的结果是它的左侧运算对象,并且是一个左值。相应的,结果的类型就是左侧运算对象的类型。如果赋值运算符的左右两个运算对象类型不同,则右侧运算对象将转换成左侧运算对象的类型
赋值运算符满足右结合律,这一点与其他二元运算符不太一样:
int ival,jval;
ival=jval=0;
- 赋值运算返回的是其左侧运算对象,所以靠右的赋值运算的结果(即
jval
)被赋给了ival
- 对于多重赋值语句中的每一个对象,它的类型或者与右边对象的类型相同、或者可由右边对象的类型转换得到
- 赋值语句经常会出现在条件当中。因为赋值运算的优先级相对较低,所以通常需要给赋值部分加上括号使其符合我们的原意。
递增和递减运算符
递增和递减运算符有两种形式:前置版本和后置版本。到目前为止,本书使用的都是前置版本,这种形式的运算符首先将运算对象加1(或减1) ,然后将改变后的对象作为求值结果。后置版本也会将运算对象加1(或减1) ,但是求值结果是运算对象改变之前那个值的副本尽量使用前置版本的递增
:::tips 前置版本的递增运算符避免了不必要的工作,它把值加1 后直接返回改变了的运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容。如果我们不需要修改前的值,那么后置版本的操作就是一种浪费。 :::
解引用与递增
后置递增运算符的优先级高千解引用运算符,因此*pbeg++
等价千*(pbeg++)
。pbeg++
把pbeg
的值加1, 然后返回pbeg
的初始值的副本作为其求值结果,此时解引用运算符的运算对象是pbeg
未增加之前的值。
:::danger 大多数运算符都没有规定运算对象的求值顺序,这在一般情况下不会有什么影响。然而,如果一条子表达式改变了某个运算对象的值,另一条子表达式又要使用该值的话,运算对象的求值顺序就很关键了。 :::
while(beg!=s.end()&&!ispace(*beg)){
*beg=toupper(*beg++)//wrong!
//1. *beg=toupper(*beg);
// beg=beg+1;
// *(beg+1)=toupper(*beg);
// 可能按照任意一个来编译!
}
成员访问
点运算符和箭头运算符都可用于访问成员,其中,点运算符获取类对象的一个成员;箭头运算符与点运算符有关,表达ptr->mem
等价于(*ptr).mem
移位运算符
移位运算符的优先级不高不低,介于中间:比算术运算符的优先级低,但比关系运算符、赋值运算符和条件运算符的优先级高。因此在一次使用多个运算符时,有必要在适业的地方加上括号使其满足我们的要求。
移位运算符也是对int型进行操作的;
逗号运算符
逗号运算符(comma_operator)含有两个运算对象,按照从左向右的顺序依次求值。和逻辑与、逻辑或以及条件运算符一样,逗号运算符也规定了运算对象求值的顺序。
对于逗号运算符来说,首先对左侧的表达式求值,然后将求值结果丢弃掉。逗号运算符真正的结果是右侧表达式的值。如果右侧运算对象是左值,那么最终的求值结果也是左值。
类型转换
:::info 当两种不同的类型相加减or进行算术运算时,C++一般会自动进行类型转换(按照一定的规则) ::: 除了一些默认的类型转换,还有一些其他的类型转换:
数组转换成指针:在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针
int ia[10];
int *p=ia;
此例中,数组名称被转换为指向数组的首元素的指针or数组首元素的地址;
指针转换成bool类型:
char* cp=get_string();if(cp)……
转换成常量:将指向非常量类型的指针转换成指向常量类型的指针e.g.
int i; const int *p=&i
显示转换
:::info 命名的强制类型转换:
cast_name<type>(expressions);
type:转换的目标类型
- cast_name:转换方式
-
cast_name:
static_cast
- dynamic_cast
- const_cast
- reinterpret_cast