C++ Primer 学习记录
昨天写博客时用的是博客园自带的 MarkDown编辑器,一点儿都不好用,插入代码块和段落缩进很难搞,传统的 MarkDown语法说四个空格或者一个 Tab就可以缩进,结果博客园自带的编辑器里这样做居然不行。再搜了很多个 MarkDown编辑器后,决定还是使用小书匠,因为这个好像可以直接把文章发布到博客园上。这就很方便了。话不多说,进入正题。 1.运算符的三个关键点:优先级、结合律、求值顺序。 2.在重载运算符时,运算对象的类型和返回值的类型可以改变,但运算对象的个数、运算符的优先级和结合律都是无法改变的。 3.decltype作用于表达式时,当表达式的求值结果是左值时,得到的是引用类型;当求值结果是右值时,得到的是值类型。
int a = 5;
int *p = &a;
decltype(*p) p1; // p1是 int &
decltype(&p) p2; // p2是 int **
4.C++语言对于大多数的运算符并没有规定求值顺序,对于这些运算符,如果表达式指向并修改了同一个对象,将会引发错误并产生未定义的行为。
int i = 0;
cout << i << " " << ++i << endl; // 未定义
编译器可能先求 ++i的值再求 i的值,也可能先求 i的值再求 i的值。此表达式的行为不可预知。有 4种运算符规定了它们的求值顺序,分别是 &&、||、条件(?:)和逗号(,)。 5.对于整数的除法和取余运算中,C11新标准中规定商一律向 0整除(即直接切除小数部分)。
21 % -5; /* 结果是 1 */ 21 / -5; /* 结果是 -4 */
-21 % -5; /* 结果是 -1 */ -21 / -5; /* 结果是 4 */
6.对于二元运算符,算术>关系>逻辑>赋值。所以在条件语句中,赋值部分通常加上括号。
if ((i = get_value()) != 42)
7.对于递增/递减运算符,优先使用前置版本,因为后置版本需要在修改前将原始值存储下来,效率更低。
8.条件运算符的优先级非常低,在输出表达式中使用条件运算符时要在两端加上括号。
cout << ((grade < 60) ? "fail" : "pass"); // 输出 pass或 fail
cout << (grade < 60) ? "fail" : "pass"; // 输出 1或者 0后根据 cout的值输出 pass或 false
9.如果运算对象是带符号的且它的值为负,那么位运算符如何处理运算对象的“符号位”依赖于机器。而且,此时的左移操作可能会改变符号位的值,是一种未定义行为。因此建议仅将位运算符用于处理无符号类型。 10.位异或运算符(^),两个运算对象相同,结果为 0,反之为 1。真值表如下
0 | 1 | |
---|---|---|
0 | 0 | 1 |
1 | 1 | 0 |
11.sizeof,并不实际计算其运算对象的值。因此,在作用于解引用的指针时,即使该指针是一个未初始化的指针也不会有影响,它返回的是所值类型的空间大小。 对 char或者类型为 char的表达式执行 sizeof运算,结果得 1。 对引用类型执行 sizeof运算,得到被引用对象所占空间的大小。 对指针执行 sizeof运算,得到指针本身所占空间的大小。 对解引用指针执行 sizeof运算,得到指针所指的对象所占空间的大小,指针不需有效。 对数组执行 sizeof运算得到整个数组所占空间的大小,并不会将数组转换为指针。 对 string或 vector对象执行 sizeof运算,只返回该类型固定部分的大小,和里面存放了多少数据无关。 12.对无符号类型和带符号类型进行运算,其结果比较复杂,也依赖于具体机器,所以应该尽量避免无符号类型和带符号类型的运算! 13.类型转换
double d = 3.14;
int i = static_cast<int>(d);
void *p = &d;
double *pd = static_cast<double*>(p); // 将 void*转换回初始的指针类型