以下知识点是我认为需要特别注意或者难以理解的知识点,大部分简单的基础知识我略过了。
1. 左值和右值
当一个对象被用作右值时,用的是对象的值(内容),当对象被用作左值时,用的是对象的身份。
2. 优先级和结合律
括号无视优先级和结合律,因此如果对某些运算符的优先级不清楚,最好用括号来强制让表达式的组合关系符合程序逻辑的要求。
有4中运算符明确规定了运算对象的求值顺序:逻辑与(&&)、逻辑或(||)、条件(?:)、逗号(,)。对于没有指定执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为。例如
int i=0;
cout << i << " " << ++i << endl; //未定义的
编译器可能先求出++i的值再求i的值,输出结果是1,也可能先求i的值再求++i的值,此时输出结果是0。因此还有一条经验准则对书写复合表达式有益:如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象。
3. 算术运算符
在C++11新标准中,规定商一律向0取整。如果m%n不等于0,则它的符号和m相同。例如
-21%-8 = -(21%8) = -5;
21%-5 = 21%5 = 1;
4. 赋值运算符
赋值运算符的左侧对象必须是一个可修改的左值。例如
int i = 0; //正确,初始化而非赋值
const int ci=i; //正确,初始化而非赋值
k=1024;
ci=k; //错误,ci是常量(不可修改的)左值
复合赋值运算符:+=,-=,*=,/=,%=,<<=,>>=,&=,^=,|=。使用方法如:
sum+=val; //等价于sum = sum+val;
sum+=val和sum = sum+val唯一的区别就是左侧运算对象的求值次数:使用复合运算符只求一次,使用普通运算则求值两次(一次是作为右边子表达式的一部分求值,另一次是作为赋值运算的左侧运算对象求值。)
5. 成员访问运算符
点运算符和箭头运算符都可以用于访问成员,例如
string s1="a string",*p=&s1;
auto n=s1.size(); //运行string对象s1的size成员
n=(*p).size(); //运行p所指对象的size成员
n=p->size(); //等价于上行代码
注意解引用运算符的优先级低于点运算符,因此*p要加括号。
6. 条件运算符
使用形式为:cond ? expr1:expr2; 首先求cond的值,如果为真,则对expr1求值,否则对expr2求值。例如
string finalgrade=(grade<60) ? "fail":"pass";
7. 移位运算符
位运算符作用于整数类型的运算对象。移位运算符(又叫IO运算符)满足左结合律。位运算符有以下几种:
~ 位取反
<< 左移
>> 右移
& 位与
^ 位异或
| 位或
8. sizeof运算符
sizeof运算符返回一条表达式或一个类型名字所占的字节数。
sizeof运算符的结果部分地依赖于其作用类型:
①对char或者类型为char的表达式执行sizeof运算,结果为1;
②对指针执行sizeof运算得到指针本身所占空间的大小;
③对解引用指针执行sizeof运算得到指针指向的对象所指空间的大小,指针不需要有效;
④对数组执行sizeof运算得到整个数组所占空间的大小,等价于对数组中所有的元素各执行一次sizeof运算并将所得结果求和;
⑤对string对象或vector对象执行sizeof运算只返回该类型固定部分的大小,不会计算对象中的元素占了多少空间。