《C++ Primer》第4章 表达式(一)

参考资料:

  • 《C++ Primer》第5版
  • 《C++ Primer 习题集》第5版

4.1 基础(P120)

表达式由一个或多个运算对象组成,对表达式求值将得到一个结果。字面量和变量是最简单的表达式。

4.1.1 基本概念(P120)

C++ 定义了若干一元运算符、二元运算符,还有一个三元运算符,函数调用也是一种特殊的运算符。

组合运算符和运算对象

对以一个复杂的表达式,我们首先要理解其运算符的优先级结合律,以及运算对象的求值顺序

运算对象的转换

小整型会被提升为大整型。

重载运算符

重载运算符时,运算对象的类型和返回值的类型是可以重新定义的,但运算对象的个数、运算符的优先级和结合律是不能改变的。

左值和右值

C++ 表达式要么是右值,要么是左值。一个左值表达式的求值结果为一个对象或者一个函数,一般而言,左值可以位于赋值语句的左侧,但以常量对象为代表的某些左值不能位于赋值语句的左侧。右值表达式的结果不能位于赋值语句的左侧,且某些右值表达式的求职结果也是对象。

当一个对象被用作右值的时候,用的是对象的;当其被用作左值的时候,用的是对象的身份(内存中的地址)。左值可以当作右值使用,而右值不能当左值使用。常见的需要使用左值的运算符:

  • 赋值运算符需要左值作为其左侧运算对象,结果同样为左值。
  • 取地址符作用于左值对象,结果为右值。
  • 内置的解引用、下标运算符、迭代器的解引用、stringvector 的下标运算符的结果都是左值,

4.1.2 优先级与结合律(P121)

高优先级运算符与运算对象的结合更加紧密,如果优先级相同,则组合规律由结合律确定。算术运算符满足左结合律,即优先级相同时,将按照从左到右的顺序计算

括号无视优先级与结合律

被括号括起来的部分会被当成一个单元来求值,然后再与其他部分组合。

4.1.3 求值顺序

优先级规定了运算对象组合方式,但没有说明运算对象按照什么顺序求值:

int i = f1() + f2();

f1f2 一定会在执行乘法前调用,但两个函数执行的先后顺序是没有规定的。

对于那些未指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,则将引发未定义行为

下面 4 种运算符明确指定了运算对象的求值顺序:&&||?:,

求值顺序、优先级、结合律

对于下面的表达式:

f() + g()*h() + j();

优先级规定了 g()h() 的返回值相乘;结合律规定了 g()h() 的乘积先和 f() 相加;这些函数的调用顺序没有规定。

书写复合表达式的两条经验:

  • 在不确定运算符优先级和结合律的情况下最好使用括号
  • 如果改变了某个运算对象的值,则在表达式的其他地方不要再使用这个对象。例外情况:*++iter

4.2 算术运算符(P124)

算术运算符的结果为右值

image-20230927105601000

一元正号和一元负号,如 +a 可以理解为 0 + a ,其中 0int 类型:

bool b = true;
bool b2 = -b;    // b2是true

当计算的结果超过该类型的表示范围时就会产生溢出,溢出的结果是不可预知的,不同的系统可能有不同的结果。

整数相除的结果为整数,小数部分直接舍弃(向 0 取整)。

对取余运算来说,在不溢出的前提下恒有 m % (-n) 等价于 m % n(-m) % n 等价于 -(m % n)

21 % -6;    // 结果为3
-21 % 6;    // 结果为-3

4.3 逻辑和关系运算符(P126)

image-20230927110605603

关系运算符作用域算术类型或指针类型,逻辑运算符作用于任何能转换成布尔值的类型。

逻辑与和逻辑和运算符

短路求值:&&|| 都是先求左侧运算对象再求右侧对象。

vector<string> text{"hello", "world"};
for(const auto &s : text){
    cout << s << endl;
}

上面的代码中,由于 string 类型对象可能较大,将 s 声明成引用可以避免对元素的拷贝。同时,由于不需要对 string 对象进行写操作,所以将 s 声明成 const

逻辑非运算符

关系运算符

关系运算符的结果为布尔值,所以其在连用时需要特别注意:

if(1 < 9 < 2)    // 结果为真

相等性测试与布尔字面值

进行比较运算时,除非比较的对象是布尔类型,否则不要使用布尔字面值。

练习

Screenshot_20230927_155143

字符串字面量等价于以 \0 结尾的字符数组,所以上面的声明等价于让 cp 指向一个字符数组的首元素。

4.4 赋值元素符(P129)

赋值运算符的左侧对象必须为可修改左值,运算结果为其左侧运算对象。如果赋值运算符两侧对象的类型不同,则右侧对象将转换为左侧对象。

C++11 允许将花括号括起来的初始值列表作为赋值语句的右侧对象:

vector<int> vi{1, 2, 3};
vi = {4, 5, 6, 7};

如果赋值运算符左侧为内置类型,则列表最多只能包含一个值,且其所占空间不应大于目标类型空间。

无论左侧对象的类型是什么,初始值列表均可以为空,此时编译器将创建一个值初始化的临时量并将其赋值给左侧对象。

赋值运算满足右结合律

i = j = 0;

赋值运算优先级较低

切勿混淆相等运算符和赋值运算符

复合赋值运算符

如:+=-= 等等。

任何一种复合运算符都等价于 a = a op b ,但复合运算符只求值一次,而使用普通的运算符还要进行一次额外的赋值。

4.5 递增和递减运算符(P131)

++-- 为对象的加 1 和减 1 提供了一种简洁的写法。此外,这两个运算符可以应用于迭代器,而很多迭代器不支持算术运算,所以递增递减运算是必要的。

递增递减运算符作用于左值对象,有前置版本和后置版本。前置版本返回将对象本身作为左值返回,后置版本将对象原始值的副本作为右值返回。

除非必须,否则不用递增递减运算符的后置版本,因为相比前置版本,后置版本需要将原始值保存下来,造成浪费。

在一条语句中混用解引用和递增运算符

auto p = v.begin();
while(p != v.end && *p >= 0){
    cout << *p++ << endl;    // 输出当前值,并将p向前移动一个元素
}

运算对象可以按任意顺序求值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值