4.1.1 基本概念
1、左值和右值
(1)C++的表达式要不然是右值(rvalue),要不然就是左值(lvalue)
(2)一个重要原则:在需要右值的地方可以用左值来替代,但不能把右值当成左值使用(有一种情况例外,可以把右值当成左值使用)。
(3)当一个对象被用作右值的时候,用的对象的值(内容),当对象被用作左值的时候,用的是对象的身份(在内存中的位置)
(4)需要用到左值的运算符
①赋值运算符(需要一个非常量左值)
②取地址符
③内置解引用运算符、下标运算符。
④内置类型和迭代器的递增减运算符
4.1.2 优先级与结合律
1、复合表达式(compound expression):是指含有两个或多个运算符的表达式。
(1)括号可以无视优先级和结合律。
4.1.3 求值顺序
1、有四种运算符明确规定了运算符对象的求值顺序
(1)&&
(2)||
(3)?:
(4),
4.3节练习
4.10:为while循环写一个条件,使其从标准输入中读取整数,遇到42时候停止。
int a;
while (cin >> a&&a != 42)
cout << a << endl;
4.11:书写一条表达式用于测试4个值a、b、c、d的关系,确保a大于b、b大于c、c大于d
int a = 4, b = 3, c = 2, d = 1;
if (a > b&&b > c&&c > d)
cout << "a>b,b>c,c>d" << endl;
1、赋值运算符满足右结合律
2、赋值运算表达式优先级比较低,所以在条件语句中,赋值部分通常应该加上括号。
1、
(1)前置版本(++i):先将运算对象加(减1),然后将改变后的对象作为求值结果。
(2)后置版本(i++):先将对象作为求值结果,再给运算对象加1(或减1)
2、运算对象可按任意顺序求值
1、格式:
condition?expr1:expr2
(1)condition为真,执行expr1
(2)condition为假,执行expr2
2、条件运算符可嵌套
3、条件运算符的优先级非常低,因此当一条长表达式中嵌套了(?:),通常需在两端加上括号。
4、7节练习
4.21:编写一段程序,使用条件运算符从vector中找到哪些元素的值是奇数,然后将这些奇数值
int a;
std::vector<int> iv;
while(cin>>a)
iv.push_back(a);
for(auto:vi)
cout<<((i%2==0)?0:i*2);
4.22:用条件运算符版本将学生成绩分为fail、low pass、pass、high pass四个阶层。
int grade;
cout<<"Enter grade:\n";
cin>>grade;
cout<<(grade<60?"fail":(grade<75?"low pass":(grade<90?"pass":"high pass")))<
用if语句写的版本
int grade;
cout<<"Enter grade: ";
cin>>grade;
if(grade<60)
cout<<"fail"<else if(grade<75&&grade>60)
cout<<"low pass"<else if(grade>75&&grade<90)
cout<<"pass"<else
cout<<"high pass"<
1、位运算符作用于整数类型的运算对象,并把运算对象看成是二进制的集合。
(1)位运算符提供检查和设置二进制位的功能
2、bitset类型,其标准库类型也可以表示任意大小的二进制位集合。
3、位运算符(满足左结合律)
(1)~(位求反)
<1>格式:
~expr
(2)<<(左移) >>(右移)
<1>格式:
expr1<
expr1>>expr2
(3)&(位与)
<1>格式:
expr1&expr2
(4)^(位异或)
<1>格式:
expr^expr
(5)|(位与)
<1>格式:
expr|expr
4、位运算符(又叫IO运算符)满足左结合律
1、作用:返回一条表达式或一个类型名所占的字节数
2、格式:
(1)
sizeof (type);
size expr;
3、sizeof运算符可获取类成员大小而无须提供一个具体的对象。
1、隐式转换(implicit conversion):由编译器自动执行的类型转换(自动执行,无须程序员介入。)
2、发生隐式转换的情况
(1)比int类型小的整型值受限提升为较大的整数类型
(2)在条件中,非布尔值转换成布尔类型
(3)初始化过程中,初始值(literal)转换成变量类型
(4)赋值语句中,右侧运算符转换成左侧运算符
(5)算术运算符或关系运算符有多种类型的时候
(6)函数调用的时候
4.11.3 显式转换
1、命名的强制类型转换(cast)
(1)格式: cast-name(expression)
<1>type:转换的目标类型
<2>expression:要转换的值
<3>type若是引用类型,则结果是左值
<4>cast-name是以下4种类型
①static_const
②dynamic_cast
③const_cast
④reinterpret_cast
中的一种
(2)static_cast:任何具有明确定义的类型转换,只要不包含底层const,都可以使用。
<1>例:
double slope=static_cast<double>(j)/i;
<2>优点:较大算术类型转换为较小算术类型。
(3)const_cast:只能改变运算对象的底层const
<1>例:
const char c='k';
char *pc=&c;
char *p=const_cast<char *>(pc);
//正确,但是通过p写值是未定义的行为
<2>优点:const_cast能去掉底层const,将一个指向常量的指针转换为一个指向不一定是常量的指针。
(4)reinterpret_cast:通常为运算对象的位模式提供较低层次上的重新解释(一般用于不同类型的指针转换)
<1>例:
int *ip;
char *pc=reinterpret_cast<char *>ip;
//pc所指向的对象为int而非字符
string str(pc); //运行时发生错误
2、旧式的强制类型转换
(1)格式:
type(expr); //函数形式的强制类型转换
(type)expr; //C风格的强制类型转换
4.11.3节练习
4.36:假设i是int类型,d是double类型,书写表达式i*=d使其执行整数类型的乘法而非浮点类型的乘法
int i=1;
double d=1.234;
i*=static_cast<double>(d);
i*=d;
cout<
4.37:用命名的强制类型转换改写下列旧式的转换语句
int i;
double d;
const string *ps;
char *pc;void *pv;
(a)pv=(void *)ps;
pv=static_cast<void *>(const_cast<string *>(ps));
(b)i=int(*pc);
i=static_cast<int>(*pc);
(c)pv=&d;
pv=static_cast<void *>(&d);
(d)pc=(char *)pv;
pc=static_cast<char *>(pv);