(四)表达式

表达式

由多个运算对象组成,对表达式求值将得到一个结果。如字面值及变量,和运算符组合起来就是较为复杂的表达式。

基础

一元运算符和二元运算符

一元运算符:作用于一个运算对象
二元运算符:作用于两个运算对象
存在三元运算符,函数调用也是一种特殊的运算符,对运算对象数目无要求。

类型转换

关联

如果两种类型有关联,需要其中一种类型的运算对象时,可以用另一种关联类型对象或值来替代,二者可以相互转换。

隐式转换
  • 类型转换自动执行,不需要程序员介入。
  • 算数转换
    整型提升:把小整数转换成较大的整数。对于无符号类型,需要进行整型提升,再将小符号转换成较大的类型。
    数组变指针:使用数组名,将自动转换成指向数组首元素的指针。当使用decltype、取地址(&)、sizeof和typeid时,上述转换不会发生。
    指针转换:0/nullptr能转成任意类型指针,任意非常量指针能转换成void*,指向任意对象的指针能转换成 const void*。
    布尔转换:判断语句(if/while)
    常量转换:非const到const
    类类型
显式转换
  • 旧式强转换模式

type(expr)
(type)expr

  • 对象显式强制转换,命名强制类型转换
cast-name<type>(expression);
  • cast-name

static_cast:不包含底层const时使用,可以用来强制转换类型,或者找回存在于void*中的指针。

	int a = 1;
	double b = static_cast<double>(a);
	int* p = &a; 
	void* vp = p;
	int *ip = static_cast<int*>(vp);  //vp中类型必须是int*

const_cast:只改变对象的底层const

	int a = 2;
	const int c = 10;
	const int* p1 = &c;
	int* p2 = const_cast<int*>(p1);
	cout << *p2 << endl;  //10
	*p2 = 1;
	cout << *p2 << endl;  //1
	cout << *p1 << endl;  //1
	p2 = &a;
	cout << *p2 << endl;  //2

reinterpret_cast:为运算对象为模式提供较低层次上的重新解释。(不推荐使用)
dynamic_cast:

重载运算符

运算符作用于类类型运算对象时,用户可以自定义其含义,为已存在运算符赋予另一层含义,称为重载运算符。
eg:IO库里的“>>”和“<<”,压入压出,逻辑运算里是右移和左移。

左值右值

  • 右值用的是对象的值(内容),左值用的是对象的身份(在内存中存在的地址)。
  • 需要右值的地方可以用左值代替,实际使用的是它的值(内容)。
 int *p;
 decltype(*p) //解引用是左值,地址是p的地址,返回类型int&
 decltype(&p) //返回p的地址,取地址符生成右值,内容是指向整形指针的指针,返回类型是int**

运算符优先级、结合律

优先级:高优先级使对象更紧密组合在一起。
结合律:优先级相同,组合规则由结合律确定。
括号无视优先级和结合律 。
运算优先级表:
C++运算符优先级

求值顺序

大多数情况下,不会指明求值顺序,且求值顺序和优先级结合律无关。考虑到这个问题,如果运算过程中改变了某个运算对象的值,在表达式其他地方不要再使用这个对象。除非求值顺序已知。

int i= f1()*f2();  //无法知道f1()和f2()谁先被调用

运算符

1.算术运算符

  • 优先级:一元“+”“-” > “*”“/”“%” > “+”“-”
  • 算术运算符运算对象和求值结果都是右值
  • 对于布尔类型运算:
	bool f = 0;
	bool f2 = -f;  //f2为0
	bool f3 = !f;  //f3为1
  • 对于负数商:(-m)/n= m/(-n) = -(m/n)
  • 对于负数余数:m%(-n) = m%n, (-m)%n=(-m)%(-n)=-(m%n)

2.逻辑关系运算符

  • 优先级:逻辑非(!)>比较(>,<,>=,<=)>相等(==,!=)>逻辑与(&&)>逻辑或(||)
  • 运算对象和求值结果都是右值
  • 对于逻辑与和逻辑或,“&&”和“||”具有短路求值策略。对于与,左侧为真才判断右侧;对于或,左侧为假才判断右侧。左侧运算是为了保证右侧运算对象求值过程的正确性和安全性。
  • 比较运算符:返回布尔值

3.赋值运算符

  • 优先级:关系运算符>赋值运算符
  • 左侧运算对象是可修改左值,结果是左值,类型是左侧运算对象的类型。
  • 赋值运算符满足右结合律
int i,j;
i = j = 0; //三者类型相同,或者可以类型转换
  • 复合赋值:普通运算符求值两次,复合运算符求值一次。
a += b;

4.递增递减运算符

++i; --i; //前置版本
i++; i--; //后置版本

  • 除非必须,不使用后置版本。当既想将对象加一或减一,又想引用它原来的值时,用后置版本。

5.成员访问运算符

  • 点运算符和箭头运算符
  • 解引用运算符优先级低于点运算符
p->mem;
(*p).mem;

6.条件运算符

  • 条件运算符

cond?expr1:expr2;

  • 嵌套条件运算符:嵌套最好不要超过2~3层

cond1?expr1:cond2?expr2:expr3

  • 条件运算符优先级非常低,使用时通常需要加括号。
cout<<((grade<60)?"fail":"pass")<<endl;
cout<<(grade<60)?"fail":"pass"<<endl; //输出grade<60的结果。
cout<<grade<60?"fail":"pass"<<endl; //输出grade结果,然后比较cout<60。

7.位运算符

  • 用于整形类型对象,把对象看作二进制位集合
  • 优先级:位取反(~)>左右移(>>,<<)>位与(&)>位亦或(^)>位或(|)
  • 对于符号位的位运算取决于机器,所以建议仅将位运算符用于处理无符号整型

8.sizeof运算符

  • 返回一条表达式或一个类型名字所占字节数。满足右结合律。所得类型size_t类型常量表达式。
sizeof(type)
sizeof expr  //返回表达式结果类型大小
  • 使用起来和deltype很像,sizeof不实际计算运算对象的值。所以对于类成员,不需要提供一个具体对象,就能获得类成员大小。
int a = 1;
int* p;
int& refa = a;
cout << sizeof(a) << endl; //int型大小,4
cout << sizeof(p) << endl; //int型指针大小,8
cout << sizeof(*p) << endl; //int型大小,4
cout << sizeof(refa) << endl;//int型大小,4
char b = '9';
cout << sizeof(b) << endl; //char型大小,1
int c[] = { 1,2,3,4,5 };
cout << sizeof(c) << endl; //数组大小,4*5 = 20
char e[] = { 'h','h','d','y','z','w','h','y','\0'};
cout << sizeof(e) << endl; //9
cout << strlen(e) << endl; //8,不算空格
string s = "hhdyzwhy";
cout << sizeof(s) << endl;//string类型固定部分大小,40
vector<int> d = {1};
cout << sizeof(d) << endl;//vector类型固定部分大小,32

9.逗号运算符

for(int i = 0;i<nums.size();++i,--cnt) //每次循环后i和cnt会发生改变
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值