/*第四章 表达式*/
**表达式是由一个或多个运算对象组成,对表达式求值得到结果,运算符和一个或多个运算对象组成表达式
**字面值和变量:最简单的表达式,结果为其字面值和变量的值
4.1 基础
4.1.1 基本概念
1. 一元运算符和二元运算符、三元运算符
**函数调用对运算对象没有数量限制
2. 组合运算符优先级和运算对象
3. 重载运算符:当运算符作用于类类型的运算对象时,可以自行定义其含义(string、vector使用的运算符等)
4. 左值和右值
**左值表达式求值结果为一个对象或者一个函数
**归纳:当一个对象被用作右值的时候,用的是对象的值(内容),当对象被用作左值是,用的是其内存中位置和身份
4.1.2 优先级与结合律
1. 复合表达式是指含有两个或多个运算符的表达式,优先级和结合律决定运算对象组合的方式
2. 优先级会影响程序的正确性
4.1.3 求值顺序*************************
1. 优先级规定运算对象的组合方式,却没有说明运算对象求值的顺序
**int i = f1() * f2(); //无法知道调用两个函数的先后顺序
2. 当没有指定执行顺序的运算符,表达式指向和修改同一个对象将引发错误和未定义行为
**int i = 0;
**cout << i << " " << ++i << endl; //未定义
**行为不可预知
4. 有4种明确规定求值顺序的运算符:
**逻辑与(&&):先求左侧运算对象的值,左侧为真则继续求右侧值
**逻辑或(||)
**条件(?:)
**逗号(,)
5. 运算对象的求值顺序与优先级和结合律无关
**f() + g() * h() + j()中,函数调用顺序同样没有明确规定
6. 书写复合表达式:
**拿不准使用括号强制明晰表达式的组合关系
**如果修改某个运算对象的值,在表达式其他地方不再使用
**例外:当改变运算对象子表达式本身就是另一个子表达式的运算对象时表达式无效
** *++iter;
4.2 算术运算符
**用于算数类型或指针
**算数运算符(左结合律)
————————————————————————————————————————————————————————————————————————————————
**运算符 功能
** + 一元正号
** - 一元负号
********************************
** * 乘法
** / 除法
** % 求余
********************************
** + 加法
** - 减法
——————————————————————————————————————————————————————————————————————————————————
1. 表达式求值前,小整数类型的运算对象被提升成较大整数类型,所有对象最终转换成同一类型
2. 布尔值不应该参与运算:对于大多数运算符,布尔类型运算对象将被提升为int类型
**bool b = true;
**bool b2 = -b; //b被提升为整数1,求值后为-1,再转化为bool类型赋值b2,b2仍为true
3. 溢出和其他算术运算异常
**数学性质本身:除数为0等
**源于计算机特点
**环绕(wrappe around):符号位本来为0,溢出被修改成1
**short value = 32767;
**value += 1; //环绕,value = -32768
4. C++11 规定商一律向0取整,直接切除小数部分
5. -m溢出特殊情况,其他情况下求余m%n不等于0,则符号与m相同
**(-m)/n = m/(-n) = -(m/n);
**m%(-n) = m/n;
**(-m)%n = -(m%n);
4.3 逻辑和关系运算符
**用于任意能转换成布尔值的类型,返回值都是布尔值
**逻辑运算符和关系运算符
——————————————————————————————————————————————————————————————
**结合律 运算符 功能
** 右 ! 逻辑非
**************************************************************
** 左 < 小于
** 左 <= 小于等于
** > 大于
** >= 大于等于
**************************************************************
** == 相等
** != 不等
**************************************************************
** && 逻辑与
**************************************************************
** || 逻辑或
——————————————————————————————————————————————————————————————
1. 短路求值:当且仅当一侧运算对象无法确定表达式的结果时才会计算另一侧对象
2. 关系运算求值结果为布尔值
**if(a<b<c) //当c>1条件为真,a<b返回一个布尔值0或者1
**if(a<b && b<c)
3. 相等性测试和布尔字面值
**进行比较运算,除非比较对象是布尔类型,否在不要使用布尔字面值作为运算对象
4.4 赋值运算符
1. 赋值运算符左侧运算对象必须是一个可以修改的左值(const int i = 0;属于初始化而不是赋值)
2. 赋值运算符的结果是它的左侧运算对象,且是一个左值
3. 赋值运算满足右结合律
**int ival,jval;
**ival = jval = 0;
4. 多重赋值语句中的每一个对象,它的类型或者与右边对象类型相同,或者可由右边对象类型转换得到
**int ival,*jval;
**ival = jval = 0; //错误:jval为指针类型的值
5. 赋值运算符优先级低于关系运算符
6. 复合赋值运算符
4.5 递增和递减运算符
**支持迭代器使用,很多迭代器不支持算术运算
1. 前置版本和后置版本
**前置:首先将运算对象加1,将改变后的对象作为求值结果
**后置:将运算对象加1,求值结果是运算对象改变之前的值的副本
**int i = 0,j;
**j=++i; //j = i = 1
**j=i++; //i = 2, j = 1
**除非必须,建议不用递增递减运算符的后置版本
2. 语句混用解引用和递增运算符:后置递增运算符优先级高于解引用运算符,但是取值运算不变
** *pbeg++ == *(pbeg++)
**pbeg++将pbeg的值加1,返回pbeg初始值副本作为求值结果,解引用初始值输出并指针向前移动一位‘
3. 递增递减运算符改变对象的值,复合表达式注意不要错用运算符
** *beg = toupper(*beg++); //beg左右都调用,右侧修改对象值,语句未定义
4.6 成员访问运算符
**点运算符和箭头运算符都可用于访问成员
1. 点运算符获取类对象的成员,箭头运算符表达:ptr->mem == (*ptr).mem
2. 解引用运算符优先级低于点运算符
4.7 条件运算符(?:)
**将简单的if-else逻辑嵌入单个表达式中
1. 表达式:cond ? expr1 : expr2;
2. 允许嵌套条件运算符,可读性下降
3. 输出表达式总使用条件运算符
**cout << grade < 60 ? "fail" : "pass"; //grade输出之后返回值为cout,将cout和60比较
**小于运算符优先级比移位运算符低
4. 满足右结合律:运算对象从右向左顺序组合,嵌套时右边(?:)才能成为左边运算符的(:)分支
4.8 位运算符
**位运算符作用于整数类型的运算对象,将对象看成二进制位的集合
**位运算符(左结合律)
**————————————————————————————————————————————————————————————————————————————————————————————
**运算符(优先级) 功能
** ~ 位求反
** << 左移
** >> 右移
** & 位与
** ^ 位异或
** | 位或
**————————————————————————————————————————————————————————————————————————————————————————————
**符号位处理没有明确规定,建议仅将位运算符用于处理无符号类型
1. 移位运算符:内置含义是对运算对象执行基于二进制位的移动操作
**首先左侧运算对象按照右侧对象要求移动指定位数,将进行移位的左侧对象的拷贝作为求值结果
**右侧对象不能为负数,值必须严格小于结果的位数
**可能会发生类型提升,移出边界的位被舍弃
**左移运算符在右侧插入值为0的二进制位
**右移运算符依赖于左侧对象类型插入0或者符号位的副本
2. 位求反运算符
**将运算对象逐位求反后生成新值
3. 位与、位或、位异或运算符
**在两个运算对象上逐位执行相应逻辑操作
**位异或:运算对象对应位置有且只有一个为1,结果为1,否则为0
**位或:对应位置有一个为1,结果为1,否则为0
**位与:对应位置都为1,结果为1,否则为0
4. 移位运算符满足左结合律
4.9 sizeof运算符
**返回一条表达式或者一个类型名字所占的字节数,满足右结合律
**返回值类型为size_t的常量表达式
1. 表达式形式: sizeof(type); sizeof expr;
2. C++11 允许使用作用域运算符获取类成员的大小
3. sizeof运算符的结果部分依赖于作用的类型
**char或者char类型表达式执行sizeof,得到1
**对引用类型,得到被引用对象所占空间大小
**对指针,得到指针本身所占空间大小
**对解引用指针,得到所指向对象空间大小
**对数组运算得到整个数组所占空间大小
**对string或者vector对象执行运算只能得到该类型固定部分的大小
4. 获取数组元素多少:
**size_t sz = sizeof(ia) / sizeof(*ia);
4.10 逗号运算符
**含有两个运算对象,按照从左向右的顺序依次求值
**规定求值顺序
**首先对左侧表达式求值,丢弃求值结果,逗号运算符求值结果是右侧表达式的值
4.11 类型转换
**类型之间有关联,当程序需要其中一种类型为对象,可以用另一种关联类型的对象或值来替代,完成相互转换
1. 隐式转换:自动执行类型转换
**大多数表达式,比int类型小的整数类型先提升为较大的整数类型
**条件中,非布尔值转换成布尔类型
**初始化过程中,初始值转换变量类型;
**赋值语句,右侧对象类型转换成左侧对象类型
**算数运算中多种类型转换成同一种类型
**函数调用发生类型转换
2. 算术转换中,运算符的运算对象转换成最宽的类型
3. 整型提升:把小整数类型转换成较大整数类型
4. 无符号类型的运算对象
**类型不一致先整型提升
**整型提升不匹配,考虑有符号和无符号是否共存
**无符号不小于有符号类型,运算对象转换成无符号
**有符号类型大于无符号类型,结果依赖于机器,无符号值能都存在有符号范围内则改成有符号,否则相反
4.11.2 其他隐式类型转换
1. 数组转换成指针
2. 指针的转换
3. 转换成布尔类型:存在从算数类型或指针类型向布尔类型自动转换的机制
4. 转换成常量:t是一种类型,可以将指向t的指针或引用分别转换成指向const t的指针或引用???
5. 类类型定义的转换
4.11.3 显示转换(强制类型转换)
1. 强制转换形式:
**cast-name<type>(expression);
**cast-name = static_cast\dynamic_cast\const_cast\reinterpret_cast
**static_cast:任何具有明确定义的类型转换,只要不包含底层const,都可以使用
**const_cast:只能改变运算对象的底层const,也只能改变常量属性,不能改变类型
**同时,只有const_cast可以改变表达式的常量属性
**reinterpret_cast:通常为运算对象位模式提供较低层次上的重新解释
2. 避免强制转换
3. 旧式强制转换
**type(expr); //函数风格
**(type)expr; //c语言风格
4.12 运算符优先级表
*/
#include <iostream>
#include <string>
#include <cstddef>
#include <vector>
using namespace std;
/*练习4.21:编写一段程序,使用条件运算符从vector<int>中找到哪些元素的值是奇数,然后将值翻倍*/
int main4_21()
{
int temp;
vector<int> ivec;
cout << "请输入适量的int类型数字(^z退出输入):" <<endl;
while(cin >> temp)
{
ivec.push_back(temp);
}
cout << "原来数值为:"<< endl;
for(auto &i : ivec)
{
cout << i << " ";
}
cout << endl;
cout << "奇数修改之后数值为:"<< endl;
for(auto &i : ivec)
{
cout << ((i % 2 == 1) ? i*2 : i )<< " ";
}
cout << endl;
return 0;
}
/*练习4.22:本节示例程序将成绩划分成high pass、pass、fail三种,扩展该程序使其进一步将60-75之间设定为low pass
要求包含两个版本,一个使用条件运算符,一个使用if语句,哪个更容易理解?
if语句更容易理解,更有层次和结构,表达较为清晰
*/
int main4_22()
{
int grade;
string finalgrade;
cout << "请输入一个分数(0-100):";
if(cin >> grade && grade >= 0)
{
finalgrade = (grade > 90) ? "high pass" : (grade < 90 && grade >75) ? "pass" :
(grade <= 75 && grade > 60) ? "low pass" : "fail";
}
cout << finalgrade << endl;
return 0;
}
/*练习题4.23 : [] > + > ?:*/
int main4_23()
{
string s = "word";
string p1 = s + (s[s.size() - 1] == 's' ? "" : "s");
}
/*练习题4.24:满足左结合律时,会产生矛盾或则都满足的情况,靠左条件运算成为靠有百年条件运算分支*/
/*练习题4.28:输出每一种内置类型所占空间的大小*/
int main4_28()
{
string a = "hello,world,safsaf ,f,asdf,dfsafsdafsdafs,dsgsa";
cout << sizeof(char) << endl;
cout << sizeof(int) << endl;
cout << sizeof(short) << endl;
cout << sizeof(long long) << endl;
cout << sizeof(signed int) << endl;
cout << sizeof(char32_t) << endl;
cout << sizeof(double) << endl;
cout << sizeof(float) << endl;
cout << sizeof(a) << endl;
return 0;
}
/*练习题4.29:推断下面代码的输出结果并说明理由,实际运行程序*/
int main4_29()
{
int x[10];
int *p=x;
cout << sizeof(x)/sizeof(*x) << endl;
cout << sizeof(*p) << endl;
return 0;
}
int main()
{
main4_29();
}
C++_Primer_Plus学习笔记-第四章
最新推荐文章于 2023-05-06 08:24:10 发布