今天是第四章节的总结,挺快的,因为我翻了翻书,还有那么多没看,顿感焦急,故赶忙开始写总结。昨天在微信上看见一篇文章,大意是谈焦虑的压力,所以我也在提醒自己,要在高质量的前提下重视效率,不能因为时间不充足就忽略了质量。
有错误 请指正 谢谢
C++提供了丰富的运算符作用于内置类型运算对象。对于自定义数据类型用重载运算符机制提供支持。
一个或者多个运算对象构造,对表达式求值得到一个结果。其中字面值和常量是最常见的表达式,其结果就是其本身。当运算对象遇到运算符就可以组合成复杂的表达式。
1.一元和二元运算符的区分:运算对象的个数。
2.组合运算符和运算对象的依据:优先级和结合律。
3.运算对象的类型转换
4.重载运算符作用于自定义数据类型。重载的过程是重新确定了运算对象的类型和返回类型,并不能改变优先级和结合律。
5.左值和右值:一个表达式不是左值就是右值。在C中,为了方便记忆,通常如此说:出现在等号左边的就是左值,出现在右边的当然的就是右值。
但是在现在的C++中,形式变的很复杂。
简单的归纳是:当一个对象被当作右值使用的时候,使用的是它的的内容(值),当作左值使用的时候,使用的是它的身份(可以理解为在内存中的位置)。需要使用右值的地方可以使用左值。
运算符对运算对象有要求,目前常见的需要使用左值的运算符有:
解引用,取地址,等号,下标,前置自增自减。
decltype处理左值右值的区别:当其中的表示式是左值的时候,返回的是引用类型。
示例:
int *p;
decltype(*p) s; //s的类型是int&,在编译器中s是必须要绑定初值的,因为是引用。
decltype(&p) p; //p的类型是int**。
1.复杂表达式:存在两个或者两个以上运算符。复杂表达式的求值依赖于优先级和结合律,但是括号会无视优先级。只有当优先级相同的时候才会用到结合律。
2.优先级和结合律的影响:
2+1*5;//*的优先级大于+号。所以等价于1+(1*5);
-----------------------------------------------
int v1,v2;
cin>>v1>>v2; //有两个运算符,但是一样,所以优先级相同。根据它的左结合律,所以等价于:
((cin>>v1)>>v2);
优先级只是规定了运算符和运算对象的组合方式,但是并未规定了运算对象的求值顺序,大多数情况下运算符是不规定对象的求值顺序的。
示例:
int i=f()*g();
//*运算符并未规定求值顺序,也就是说我们并不知道哪个函数会被先调用。
对于那些并没有指定执行顺序的运算符来说,当在同一个表达中修改了一个运算对象,并且在此表达式后面还会用到此对象,那么会产生未定义行为。
示例:
int i=1;
cout<
我们知道他们的结合方式,左结合。但是表达式的执行过程是要修求出i和++i的值。那么到底是先求出i然后求++i的,还是到过来。我们并不清楚,所以产生未定义行为。
在我的vs2013 中,求值顺序是从右到左的。
#include
using namespace std;
int main(){
int i = 1;
cout << i << ++i << endl;
}
// output:22.
四个规定了求值顺序的运算符:
&& || , ?: 这四个运算符是规定了求值顺序的。比如我们已经知道的布尔逻辑(短路逻辑)
总而言之,运算对象的求值顺序与优先级和结合律是无关的。一般求值顺序是不影响表达式的最终结果。
tips:
1.当你拿不准优先级的时候请使用括号。
2.改变运算对象的表达式中不要在其他地方继续使用此对象。但是存在一个例外
示例:
*++iter;
*iter++;
在这个例子中,改变运算对象的子表达式的值就是另一个子表达式的运算对象,不影响使用。有的书是如下解释的,虽然++ 和* 的优先级一样,但是右结合的,所以等价于:
*(iter++)
优先级:一元+ -号>乘除>加减。
结合律:左结合
求值结果都是右值。
示例:
bool b=true;
bool b2=-b;
//会发生什么呢?
这个地方会发生类转换。最终的结果会是b2=1(true);
值环绕行为:符号位由0变为1.
示例:
#include
using namespace std;
int main(){
short var = 32767;
var += 1;
cout << var<< endl;
}
//output: -32768.
因为short 的取值范围是 [-32768,32767]。所以加了会越界,故发生了值环绕。
m=(m/n)*n+(m%n);
-m/n;m/(-n) 等价于:-(m/n);
m%(-n) 等价于:m%n;
(-m)%n等价于:-(m%n);
有 的是C++11 的规定,请不要奇怪。
tips:除非比较的对象是布尔型,否则不要用布尔值进行比较。推荐使用简写。
示例:
if(var==1)
if(var==true)
//除非var是布尔值,否则不推荐写成下面的形式。
8.赋值运算符:
左侧运算对象必须为非常量左值。
两个运算对象类型不同时,右侧运算对象会转换成左侧类型后,然后进行运算。
窄化转换:C++11 规定的列表初始化行为会报错。
示例:
int i={3.14}
//窄化转换,报错。
当左侧对象是内置类型时,初始化列表最多只能有一个值,自定义类型不受限制。
结合律:左结合。
优先级:优先级较低,注意使用括号。
复合赋值运算符:
+= -= *= /=
不要局限的认为就这几个,上面是常见的。
等价于:a=a op b;
区别:复合运算符的左侧运算对象求值一次,而普通的是两次,注意效率哦。
1.不要单词的以为自增自减只是为了书写简洁。迭代器部分有大用。因为有的迭代器不支持算术加减,但是支持自增自减。
示例:
*p.func();
(*p).func();
二者辨析:不同。因为.的优先级高于* ,所以上面的那个等价于:
*(p.func())
//错误的解引用行为。
求值结果是左值还是右值取决于参与运算的对象是左值还是右值。规定了求值顺序。可以嵌套。
格式:
condition? expr1:expr2
注意: expr1 和expr2 必须是关联类型的表达式,包括同类型。
优先级:优先级比较低,注意用括号。
示例:
(grade>=90)?“excellent":((grade<60)?”fail":"good")
正 好划分成三段,一般不要多层嵌套,难读。
运算对象:整数类型,并且把对象看成是二进制位集合。
1.移位(<< >>0)左移补0,右移视情况而定。无符号是补0,有符号补0或者1.
2.求反,位与,位或,位异或。位异或运算规则:对应位只有一个1,才是1,否则0.
重载改变不了结合律和优先级。所以可以参照输出运算符。
格式: sizeof(type) sizeof expr
结合律:右结合。
结果类型: size_t.
示例:
sizeof *p;
* 和sizeof 优先级相同。根据右结合律,等价于:
sizeof(*p);
tips:sizeof 运算符不会实际求出运算对象的值,因此里面即使放了一个无效指针也不影响。
C++11 规定类可以使用域运算符获取类成员的大小,无需声明对象获取成员的操作。
sizeof 只可以求出类中固定部分的大小。关于这个可以看深度探索C++对象模型博文。
规定了求值顺序。
两类:
隐式类型转换和显示类型转换
一个原则:尽可能不丢失精度,所以一般往高精度转换。
算术转换:规则定义了一套类型转换的层次:将运算对象转换成最宽类型。
整形提升:小整形转换为能容纳的最小大类型。小类型泛指bool,char 类型。
两个情况:
同号整数:往高精度类型转换。
不同号:具体情况依赖于机器。详细请翻书。
这个地方说的号不是正负,是带符号和不带符号。
3.12L+'a' ;
//不要以为'a'直接变成long double ,'a'先提升成int,然后转换为long double.
四种强制类型转换:
1.static_cast(expr)
不包含底层const 的情况下,任何具有转换意味的地方都可以用。
2.const_cast(expr)
号称去底层const转换。
3.dynamic_cast(expr)
4.reinterpret_cast(expr)
tips: 强制类型转换会干扰类型检查,慎用。当用强转时,会不在报警告信息。
旧式C风格转换:
1.type(expr);
2.(type)expr;
4.1
Output: 105
4.2
a) *(vev.begin());
b) (*(vec.begin()))+1;
4.3
可以接受啊。前提是我的程序执行的结果是不受影响的。
因为系统间的自己协调说不定比认为规定更有效率。
4.4
((12/3)*4)+(5*15)+((24%4)/2)
Output: 91
#include
using namespace std;
int main(){
cout << 12 / 3 *4+5 * 15+24 % 4/ 2;
system("pause");
return 0;
}
4.5
a) -86 b) -18
c) 0 d) -2
4.6
int i;
i%2==0;
4.7
超出其容纳范围。
short var=32767;
var+=1;
-----
char x = 256;
---------
int v = INT_MAX;
cout << v+1;
4.8
逻辑与:当前仅当左边求值为真的时候才对右边进行求值。
逻辑或:当前仅当左边求值为假的时候才对左边进行求值
相等性运算符:未规定,由具体平台决定。
4.9
先判断cp是否是空指针,若cp是空指针,不用判断右边运算对象。
若cp不是空指针,那么将对右边进行求值。
4.10
int var;
while(cin>>var&&var!=42)
4.11
(a>b)&&(b>c)&&(c>d)
4.12
小于的优先级大于!=.
所以等价于:
i!=(j
4.13
a) i=3; d=3.0;
b) i=3; d=3.5;
4.14
首先 i 未定义。
加上i 已经定义。
第一个if中, i不可能出现在42 不可能出现在等号左侧。
第二个if中,if条件永远为真。
4.15
pi=0;
ival=0;
dval=0;
原因:指针类型无法转换成int 型。
4.16
a)因为赋值运算符优先级比较低。应该有括号括起来。
修改: if((p=getPtr())!=0)
b)误用相等比较符号和赋值符号。
修改:if(i==1024)
4.17
前置自增:直接返回的自增的对象自身。
后置自增:返回的是自增的对象未修改之前的副本。
4.18
可能会无法输出第一个元素。
甚至可能输出一个不存在的元素。
因为它的模型是用前一个元素的值进行判断,但是输出后一个元素的值。
4.19
a) ptr 非空的前提下先对ptr自增,然后解引用自增之前的指针。
如果ptr是空的,那么后面的操作不会进行。
b) 先对ival进行后置自增,如果ival的值不是0.
那么测试右边运算对象的值,此时的ival已经是自增过的值。
因为规定了求值顺序,所以不影响。
c)未规定求值顺序.
修改:vec[ival]<=vec[ival+1];
4.20
a) 合法。先对迭代器后置自增,然后解引用。
b) 合法。先解引用迭代器的值,然后对其值后置自增。
c) 不合法。修改:(*iter).empty();
d) 合法。 用迭代器调用string 的empty函数。
e) 合法。先解引用迭代器,然后对其值进行前置自增。
f) 合法。用自增的迭代器调用成员函数。这个地方有点疑问?
后来我上网查了下答案,答案是如此说的,iter先调用成员函数,然后加1.
还是符合优先级的规律的。
#include
#include
#include
using namespace std;
int main(){
vector svec = { "hi", "hay"};
vector::iterator iter;
iter = svec.begin();
cout<< *iter << endl; //输出hi
if (iter++->empty()){
cout << *iter << endl;
cout << "Empty";
}
else{
cout << *iter << endl; //输出hay
cout << "Non-empty";
}
cout << endl << *iter << endl;
system("pause");
return 0;
}
4.21
#include
#include
#include
using namespace std;
int main(){
vector ivec = { 1, 2, 3, 4 };
for (auto &x : ivec){
if (x % 2 != 0)
x = 2 * x;
else
continue;
}
for (auto x : ivec)
cout << x << " ";
system("pause");
return 0;
}
4.22
#include
#include
#include
using namespace std;
int main(){
int grade = 0;
cin >> grade;
while (grade < 0 || grade>100){
cout << "Wrong Input and Enter again" << endl;
cin >> grade;
}
cout << ((grade >= 90) ? "High Pass" :((grade >= 75) ? "Pass" :((grade >= 60) ? "Low Pass" : "Fail")));
system("pause");
return 0;
}
#include
#include
#include
using namespace std;
int main(){
int grade = 0;
cin >> grade;
while (grade < 0 || grade>100){
cout << "Wrong Input and Enter again" << endl;
cin >> grade;
}
if (grade >= 90)
cout << "High Pass" << endl;
else if (grade < 90 && grade >= 75)
cout << "Pass" << endl;
else if (grade >= 60&&grade < 75)
cout << "Low Pass" << endl;
else
cout << "Fail" << endl;
system("pause");
return 0;
}
4.24
题目有错误。因为无法编译通过是因为用字符串和字符进行比较。
4.25
output:-1146. //暂时没搞明白为何会出现这个值。
4.26
因为unsigned int 16占 16位,无法表示所有人。
4.27
假设unsigned long 占32 位:
//u11:0000 0000 0000 0000 0000 0000 0000 0011
//u12:0000 0000 0000 0000 0000 0000 0000 0111
//&
//0000 0000 0000 0000 0000 0000 0000 0011
//|
//0000 0000 0000 0000 0000 0000 0000 0111
u11&&u12:1;
u11||u21:1;
4.28
cout<
4.29
ouput: 10,1.
#include
#include
using namespace std;
int main(){
int x[10], *p = x;
cout << sizeof(x) / sizeof(*x) << endl;
cout << sizeof(p) / sizeof(*p) << endl;
system("pause");
return 0;
}
4.30
a)(sizeof x)+y;
b)sizeof(p->mem[i])
c)(sizeof a)
4.31
提高效率。
vector::size_type cnt=ivec.size();
for(vector::size_type ix=0;
ix!=ivec.size();ix++,cnt--){
ivex[ix]=cnt;
}
4.32
遍历数组
4.33
根据SomaValue 的值确定程序执行。
若能求值最后转换成布尔值1,那么想x,y进行前置自增。否则x,y前置自减。
4.34
a) float 到int 到bool.
b)ival 到float,然后float 到double.
c)cval 到int,然后int到double.
4.35
a)有。'a'先提示为int,然后转成char.
b)有。ival 变为double,然后ui转换为int.最后转为float
c)有。具体情况而定。
d)有。具体情况而定。因为无符号有和符合碰到一起,要看所占字节数进行转换。
4.36
i*=static_cast(d);
4.37
a)pv=reinterpret_castps;
b)i=static_cat(*pc);
c)pv=static_cast(&d);
d)pc=static_castpv;
4.38
把j/i的值强制转换成double赋给slope。
真是写了一下午啊。我从8点开始写习题的,一直写到11.30。