C++ Primer 笔记+习题解答(四)

今天是第四章节的总结,挺快的,因为我翻了翻书,还有那么多没看,顿感焦急,故赶忙开始写总结。昨天在微信上看见一篇文章,大意是谈焦虑的压力,所以我也在提醒自己,要在高质量的前提下重视效率,不能因为时间不充足就忽略了质量。

有错误 请指正 谢谢

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的值。那么到底是先求出i然后求++i的,还是到过来。我们并不清楚,所以产生未定义行为。

在我的vs2013 中,求值顺序是从右到左的。

#include 
using namespace std;
int main(){
	int i = 1;
	cout << i << ++i << endl;
}
// output:22.
    四个规定了求值顺序的运算符:

&& || , ?: 这四个运算符是规定了求值顺序的。比如我们已经知道的布尔逻辑(短路逻辑)

总而言之,运算对象的求值顺序与优先级和结合律是无关的。一般求值顺序是不影响表达式的最终结果。

   tips:

1.当你拿不准优先级的时候请使用括号。

2.改变运算对象的表达式中不要在其他地方继续使用此对象。但是存在一个例外

示例:

*++iter;
*iter++;
在这个例子中,改变运算对象的子表达式的值就是另一个子表达式的运算对象,不影响使用。有的书是如下解释的,虽然++ 和* 的优先级一样,但是右结合的,所以等价于

*(iter++)

6.算术运算符:

  优先级:一元+ -号>乘除>加减。

  结合律:左结合

  求值结果都是右值。

示例:

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]。所以加了会越界,故发生了值环绕。
%运算符运算对象必须是整形。负商会直接切除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 规定的列表初始化行为会报错。

示例:

int i={3.14}
//窄化转换,报错。
  当左侧对象是内置类型时,初始化列表最多只能有一个值,自定义类型不受限制。

  结合律:左结合。

  优先级:优先级较低,注意使用括号。

  复合赋值运算符:

+=  -= *= /= 
不要局限的认为就这几个,上面是常见的。
等价于:a=a op b;
  区别:复合运算符的左侧运算对象求值一次,而普通的是两次,注意效率哦。

9.自增自减运算符:

  1.不要单词的以为自增自减只是为了书写简洁。迭代器部分有大用。因为有的迭代器不支持算术加减,但是支持自增自减。

10 .运算符

示例

*p.func();
(*p).func();
  二者辨析:不同。因为.的优先级高于* ,所以上面的那个等价于:

*(p.func())
//错误的解引用行为。
  求值结果是左值还是右值取决于参与运算的对象是左值还是右值

11.条件运算符:

  规定了求值顺序。可以嵌套。

格式:

condition? expr1:expr2
  注意: 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 类型。

  两个情况:

同号整数:往高精度类型转换。

不同号:具体情况依赖于机器。详细请翻书。

这个地方说的号不是正负,是带符号和不带符号。

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;

16.习题解答:

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。

End

你可能感兴趣的:(读书笔记,C++,Primer,读书笔记)