C++ Primer 笔记+习题解答(四)
2015-01-25 20:11
218 查看
今天是第四章节的总结,挺快的,因为我翻了翻书,还有那么多没看,顿感焦急,故赶忙开始写总结。昨天在微信上看见一篇文章,大意是谈焦虑的压力,所以我也在提醒自己,要在高质量的前提下重视效率,不能因为时间不充足就忽略了质量。
有错误 请指正 谢谢
2.组合运算符和运算对象的依据:优先级和结合律。
3.运算对象的类型转换
4.重载运算符作用于自定义数据类型。重载的过程是重新确定了运算对象的类型和返回类型,并不能改变优先级和结合律。
5.左值和右值:一个表达式不是左值就是右值。在C中,为了方便记忆,通常如此说:出现在等号左边的就是左值,出现在右边的当然的就是右值。
但是在现在的C++中,形式变的很复杂。
简单的归纳是:当一个对象被当作右值使用的时候,使用的是它的的内容(值),当作左值使用的时候,使用的是它的身份(可以理解为在内存中的位置)。需要使用右值的地方可以使用左值。
运算符对运算对象有要求,目前常见的需要使用左值的运算符有:
解引用,取地址,等号,下标,前置自增自减。
decltype处理左值右值的区别:当其中的表示式是左值的时候,返回的是引用类型。
示例:
2.优先级和结合律的影响:
示例:
对于那些并没有指定执行顺序的运算符来说,当在同一个表达中修改了一个运算对象,并且在此表达式后面还会用到此对象,那么会产生未定义行为。
示例:
在我的vs2013 中,求值顺序是从右到左的。
四个规定了求值顺序的运算符:
&& || , ?: 这四个运算符是规定了求值顺序的。比如我们已经知道的布尔逻辑(短路逻辑)
总而言之,运算对象的求值顺序与优先级和结合律是无关的。一般求值顺序是不影响表达式的最终结果。
tips:
1.当你拿不准优先级的时候请使用括号。
2.改变运算对象的表达式中不要在其他地方继续使用此对象。但是存在一个例外
示例:
结合律:左结合
求值结果都是右值。
示例:
值环绕行为:符号位由0变为1.
示例:
因为short 的取值范围是 [-32768,32767]。所以加了会越界,故发生了值环绕。
%运算符运算对象必须是整形。负商会直接切除0.
示例:
左侧运算对象必须为非常量左值。
两个运算对象类型不同时,右侧运算对象会转换成左侧类型后,然后进行运算。
窄化转换:C++11 规定的列表初始化行为会报错。
示例:
结合律:左结合。
优先级:优先级较低,注意使用括号。
复合赋值运算符:
区别:复合运算符的左侧运算对象求值一次,而普通的是两次,注意效率哦。
二者辨析:不同。因为.的优先级高于* ,所以上面的那个等价于:
格式:
优先级:优先级比较低,注意用括号。
示例:
正 好划分成三段,一般不要多层嵌套,难读。
1.移位(<< >>0)左移补0,右移视情况而定。无符号是补0,有符号补0或者1.
2.求反,位与,位或,位异或。位异或运算规则:对应位只有一个1,才是1,否则0.
重载改变不了结合律和优先级。所以可以参照输出运算符。
结合律:右结合。
结果类型: size_t.
示例:
C++11 规定类可以使用域运算符获取类成员的大小,无需声明对象获取成员的操作。
sizeof 只可以求出类中固定部分的大小。关于这个可以看深度探索C++对象模型博文。
隐式类型转换和显示类型转换
一个原则:尽可能不丢失精度,所以一般往高精度转换。
算术转换:规则定义了一套类型转换的层次:将运算对象转换成最宽类型。
整形提升:小整形转换为能容纳的最小大类型。小类型泛指bool,char 类型。
两个情况:
同号整数:往高精度类型转换。
不同号:具体情况依赖于机器。详细请翻书。
这个地方说的号不是正负,是带符号和不带符号。
不包含底层const 的情况下,任何具有转换意味的地方都可以用。
号称去底层const转换。
tips: 强制类型转换会干扰类型检查,慎用。当用强转时,会不在报警告信息。
旧式C风格转换:
4.4
End
有错误 请指正 谢谢
1. 引言:
C++提供了丰富的运算符作用于内置类型运算对象。对于自定义数据类型用重载运算符机制提供支持。2.表达式:
一个或者多个运算对象构造,对表达式求值得到一个结果。其中字面值和常量是最常见的表达式,其结果就是其本身。当运算对象遇到运算符就可以组合成复杂的表达式。3.基本概念:
1.一元和二元运算符的区分:运算对象的个数。2.组合运算符和运算对象的依据:优先级和结合律。
3.运算对象的类型转换
4.重载运算符作用于自定义数据类型。重载的过程是重新确定了运算对象的类型和返回类型,并不能改变优先级和结合律。
5.左值和右值:一个表达式不是左值就是右值。在C中,为了方便记忆,通常如此说:出现在等号左边的就是左值,出现在右边的当然的就是右值。
但是在现在的C++中,形式变的很复杂。
简单的归纳是:当一个对象被当作右值使用的时候,使用的是它的的内容(值),当作左值使用的时候,使用的是它的身份(可以理解为在内存中的位置)。需要使用右值的地方可以使用左值。
运算符对运算对象有要求,目前常见的需要使用左值的运算符有:
解引用,取地址,等号,下标,前置自增自减。
decltype处理左值右值的区别:当其中的表示式是左值的时候,返回的是引用类型。
示例:
int *p; decltype(*p) s; //s的类型是int&,在编译器中s是必须要绑定初值的,因为是引用。 decltype(&p) p; //p的类型是int**。
4.优先级和结合律:
1.复杂表达式:存在两个或者两个以上运算符。复杂表达式的求值依赖于优先级和结合律,但是括号会无视优先级。只有当优先级相同的时候才会用到结合律。2.优先级和结合律的影响:
2+1*5;//*的优先级大于+号。所以等价于1+(1*5); ----------------------------------------------- int v1,v2; cin>>v1>>v2; //有两个运算符,但是一样,所以优先级相同。根据它的左结合律,所以等价于: ((cin>>v1)>>v2);
5.求值顺序:
优先级只是规定了运算符和运算对象的组合方式,但是并未规定了运算对象的求值顺序,大多数情况下运算符是不规定对象的求值顺序的。示例:
int i=f()*g(); //*运算符并未规定求值顺序,也就是说我们并不知道哪个函数会被先调用。
对于那些并没有指定执行顺序的运算符来说,当在同一个表达中修改了一个运算对象,并且在此表达式后面还会用到此对象,那么会产生未定义行为。
示例:
int i=1; cout<<i<<++i<<endl; //会产生未定义行为。我们知道他们的结合方式,左结合。但是表达式的执行过程是要修求出i和++i的值。那么到底是先求出i然后求++i的,还是到过来。我们并不清楚,所以产生未定义行为。
在我的vs2013 中,求值顺序是从右到左的。
#include <iostream> using namespace std; int main(){ int i = 1; cout << i << ++i << endl; } // output:22.
四个规定了求值顺序的运算符:
&& || , ?: 这四个运算符是规定了求值顺序的。比如我们已经知道的布尔逻辑(短路逻辑)
总而言之,运算对象的求值顺序与优先级和结合律是无关的。一般求值顺序是不影响表达式的最终结果。
tips:
1.当你拿不准优先级的时候请使用括号。
2.改变运算对象的表达式中不要在其他地方继续使用此对象。但是存在一个例外
示例:
*++iter; *iter++;在这个例子中,改变运算对象的子表达式的值就是另一个子表达式的运算对象,不影响使用。有的书是如下解释的,虽然++ 和* 的优先级一样,但是右结合的,所以等价于:
*(iter++)
6.算术运算符:
优先级:一元+ -号>乘除>加减。结合律:左结合
求值结果都是右值。
示例:
<span style="color:#330099;">bool b=true; bool b2=-b; //会发生什么呢?</span>这个地方会发生类转换。最终的结果会是b2=1(true);
值环绕行为:符号位由0变为1.
示例:
#include <iostream> using namespace std; int main(){ short var = 32767; var += 1; cout << var<< endl; } //output: -32768.
因为short 的取值范围是 [-32768,32767]。所以加了会越界,故发生了值环绕。
%运算符运算对象必须是整形。负商会直接切除0.
m=(m/n)*n+(m%n); -m/n;m/(-n) 等价于:-(m/n); m%(-n) 等价于:m%n; (-m)%n等价于:-(m%n);有 的是C++11 的规定,请不要奇怪。
7.逻辑和关系运算符:
tips:除非比较的对象是布尔型,否则不要用布尔值进行比较。推荐使用简写。示例:
if(var==1) if(var==true) //除非var是布尔值,否则不推荐写成下面的形式。8.赋值运算符:
左侧运算对象必须为非常量左值。
两个运算对象类型不同时,右侧运算对象会转换成左侧类型后,然后进行运算。
窄化转换:C++11 规定的列表初始化行为会报错。
示例:
<span style="font-size:18px;">int i={3.14} //窄化转换,报错。</span>当左侧对象是内置类型时,初始化列表最多只能有一个值,自定义类型不受限制。
结合律:左结合。
优先级:优先级较低,注意使用括号。
复合赋值运算符:
+= -= *= /= 不要局限的认为就这几个,上面是常见的。 等价于:a=a op b;
区别:复合运算符的左侧运算对象求值一次,而普通的是两次,注意效率哦。
9.自增自减运算符:
1.不要单词的以为自增自减只是为了书写简洁。迭代器部分有大用。因为有的迭代器不支持算术加减,但是支持自增自减。10 .运算符
示例:*p.func(); (*p).func();
二者辨析:不同。因为.的优先级高于* ,所以上面的那个等价于:
*(p.func()) //错误的解引用行为。求值结果是左值还是右值取决于参与运算的对象是左值还是右值。
11.条件运算符:
规定了求值顺序。可以嵌套。格式:
<span style="font-size:18px;">condition? expr1:expr2</span>注意: expr1 和expr2 必须是关联类型的表达式,包括同类型。
优先级:优先级比较低,注意用括号。
示例:
(grade>=90)?“excellent":((grade<60)?”fail":"good")
正 好划分成三段,一般不要多层嵌套,难读。
12.位运算符:
运算对象:整数类型,并且把对象看成是二进制位集合。1.移位(<< >>0)左移补0,右移视情况而定。无符号是补0,有符号补0或者1.
2.求反,位与,位或,位异或。位异或运算规则:对应位只有一个1,才是1,否则0.
重载改变不了结合律和优先级。所以可以参照输出运算符。
13.sizeof 运算符:
格式: sizeof(type) sizeof expr结合律:右结合。
结果类型: size_t.
示例:
sizeof *p; * 和sizeof 优先级相同。根据右结合律,等价于: sizeof(*p);tips:sizeof 运算符不会实际求出运算对象的值,因此里面即使放了一个无效指针也不影响。
C++11 规定类可以使用域运算符获取类成员的大小,无需声明对象获取成员的操作。
sizeof 只可以求出类中固定部分的大小。关于这个可以看深度探索C++对象模型博文。
14.逗号运算符:
规定了求值顺序。15.类型转换:
两类:隐式类型转换和显示类型转换
一个原则:尽可能不丢失精度,所以一般往高精度转换。
算术转换:规则定义了一套类型转换的层次:将运算对象转换成最宽类型。
整形提升:小整形转换为能容纳的最小大类型。小类型泛指bool,char 类型。
两个情况:
同号整数:往高精度类型转换。
不同号:具体情况依赖于机器。详细请翻书。
这个地方说的号不是正负,是带符号和不带符号。
<span style="font-size:18px;"><span style="color:#330099;">3.12L+'a' ;</span> //不要以为'a'直接变成long double ,'a'先提升成int,然后转换为long double.</span>四种强制类型转换:
<span style="font-size:18px;">1.static_cast<type>(expr)</span>
不包含底层const 的情况下,任何具有转换意味的地方都可以用。
<span style="font-size:18px;">2.const_cast<type>(expr)</span>
号称去底层const转换。
<span style="font-size:18px;">3.dynamic_cast<type>(expr) 4.reinterpret_cast<type>(expr)</span>
tips: 强制类型转换会干扰类型检查,慎用。当用强转时,会不在报警告信息。
旧式C风格转换:
<span style="color:#330099;">1.type(expr);</span> 2.(type)expr;
16.习题解答:
4.1<span style="font-size:24px;">Output: 105</span>4.2
<span style="font-size:24px;">a) *(vev.begin()); b) (*(vec.begin()))+1;</span>4.3
<span style="font-size:24px;">可以接受啊。前提是我的程序执行的结果是不受影响的。 因为系统间的自己协调说不定比认为规定更有效率。</span>
4.4
((12/3)*4)+(5*15)+((24%4)/2) Output: 91 #include <iostream> 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) -24.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<k); 由具体情况i,j,k的值确定。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 <iostream> #include <vector> #include <string> using namespace std; int main(){ vector<string> svec = { "hi", "hay"}; vector<string>::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 <iostream> #include <vector> #include <string> using namespace std; int main(){ vector<int> 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 <iostream> #include <vector> #include <string> 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 <iostream> #include <vector> #include <string> 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<<sizeof(char)<<sizeof(int) <<sizeof(bool)<<sizeof(float) <<sizeof(double);4.29
ouput: 10,1. #include <string> #include <iostream> 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)<b; d)sizeof(f());4.31
提高效率。 vector<int>::size_type cnt=ivec.size(); for(vector<int>::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<int>(d);4.37
a)pv=reinterpret_cast<void*>ps; b)i=static_cat<int>(*pc); c)pv=static_cast<void*>(&d); d)pc=static_cast<char*>pv;4.38
把j/i的值强制转换成double赋给slope。
后记:
真是写了一下午啊。我从8点开始写习题的,一直写到11.30。End
相关文章推荐
- C++ Primer 笔记+习题解答(十一)
- C++ Primer 笔记+习题解答(十)
- C++ Primer 笔记+习题解答(八)
- C++ Primer 笔记+习题解答(二)
- C++ Primer 笔记+习题解答(九)
- C++ Primer 笔记+习题解答(五)
- C++ Primer 笔记+习题解答(一)
- C++ Primer 笔记+习题解答(六)
- C++ Primer 笔记+习题解答(三)
- C++ Primer 笔记+习题解答(七)
- Introduction to Algorithms 算法导论 第1章 基础知识 学习笔记及习题解答
- C++ primer习题笔记第4章
- C++ Primer(第五版)读书笔记 & 习题解答 --- Chapter 2
- C++ primer习题笔记第7~9章
- C++ primer 习题笔记第5~6章
- 《算法导论》习题解答搬运&&学习笔记 索引目录
- Introduction to Algorithms 算法导论 第3章 函数的增长 学习笔记及习题解答
- C++ Primer(第五版)读书笔记 & 习题解答 --- Chapter 1
- DirectX 11游戏编程学习笔记之8: 第6章Drawing in Direct3D(在Direct3D中绘制)(习题解答)
- C++ primer 习题笔记第13~15章