《C++ primer plus》读书笔记(二)
作者:钱书康
第五章
1、for循环——for(initialization; test-expression; update-expression) body // test-expression 会被转换为bool,0为false,非零为true
2、表达式——表达式是值或值与运算符的组合。赋值表达式的值为其左侧成员的值,而赋值运算符是从右到左结合的。
3、a++和++a——
(1)对于内置类型,两种执行效率相同。
(2)若重载运算符,对于类而言,前缀将值加1,返回结果;后缀会复制一个副本,加1后返回副本。所以前缀比后缀效率高。
4、逗号运算符——
(1)for循环中,将多个表达式合并为一个: i++, j++;
(2)声明: int i , j;
(3)逗号表达式先计算第一个表达式,再计算第二个表达式。逗号表达式的值是第二部分的值。
(4)逗号表达式是优先级最低的表达式。
5、strcmp()——比较两个字符串。接受2个字符串地址A、B作为参数。AB相同返回0,A的字母顺序在B之前,者返回负数,否则返回正数。
(用引号括起的字符串常量是其地址。)
6、clock()——返回程序开始执行后所用的系统时间。这个值除以CLOCKS_PER_SEC可以得到秒数。
7、类型别名——#define AA char // 用AA作为char的别名,所有的AA将被char替代
或 typedef AA char
8、cin——cin.get()会忽略空格和换行符。发送给cin的输入会被缓冲。按下回车键,输入的内容才会被发送给程序。
cin.get(ch)会得到每个字符。其参数声明为引用类型,所以函数可以修改其参数的值。
9、EOF——很多PC编程环境都将Ctrl+Z视为模拟的EOF,检测到EOF之后,cin将两位(eofbit和failbit)都设置为1。eof()和fail()用来查看是否被设置。
所以循环等待输入的条件可以这样设置:while( cin.fail() == false ) {} 或 whle( !cin.fail() ){} 或 while(cin){} 或 while( cin.get(ch) ){}
(通常,EOF被定义为-1)
第六章
10、运算符——!运算符的优先级高于所有的关系运算符和算术运算符。
逻辑AND运算符的优先级高于逻辑OR运算符。
C++确保程序从左到右计算逻辑表达式。
11、cctype——字符函数库。如isalpha(ch)判断字符是不是字母,是字母就返回非零,否则返回0。
12、文本IO——使用cin进行输入时,程序将输入看作一系列的字节,其中每个字节被解释为字符编码。
第七章
13、定义函数——
(1)、无返回值: void functionName(parameterList) {}
(2)、有返回值: typeName functionName(parameterList) {}
(注意!返回值的类型不能是数组,可以是其他任何类型)
14、函数原型——
(1)、函数原型能极大降低程序出错的几率、提高效率。
(2)、函数原型不要求提供变量名,有类型列表就足够了。
(3)、括号为空与括号中使用void是等效的,不指定参数列表应使用省略号——void haha(...);
15、函数和二维数组——指针的类型是指把指针声明语句中的指针名字去掉所剩下的部分
对于: int data[3][4] = {{1,2,3,4},{5,6,7,8},{4,3,2,1}}; int total = sum(data,3); sum的原型是什么?
(1)、原型是: int sum ( int (*a) [4] , int size);
所以 int(*)[4]即,将这个指针指向int[4]。所以data的类型指向由4个int组成的数组的指针
所以 int *a[4] 的类型是int * [4],这个指针指向int,总共有4个,即它是4个指向int指针组成的数组。
(2)、函数定义: int sum (int a[][4] ,int size);
a[ r ][ c ] = *( *( a + r ) + c);
16、递归——每个递归调用都创建自己的一套变量。
(注意!C++不允许main()调用自己。)
17、函数指针——
(1)、函数地址: 函数的地址是存储其机器语言代码的内存的开始地址。如果think()是一个函数,那么think就是它的地址。
(2)、声明指针函数: 函数:double pam(int); 指针函数为: double (*pf)(int) = pam; // pf是一个指向函数的指针。
第八章
18、编译过程——编译过程的最终产品是可执行程序(由一组机器语言指令组成)。
运行程序时,操作系统将这些指令载入到计算机内存中,因此每条指令都有特定的内存地址。计算机随后逐步执行这些指令。
19、函数调用——执行到函数调用指令时,程序将在函数调用后,立即存储该指令的内存地址,并将函数参数复制到堆栈,跳到标记函数起点的内存单元,
执行函数代码(也许还需将返回值放入寄存器),然后跳回到地址被保存的指令处。
20、内联函数——编译器使用相应的函数代码代替函数调用。
函数声明前加上关键字inline,函数定义前加上关键字inline。通常将省略原型,原型处直接定义。
21、引用变量——主要用途是作函数的参数,函数将使用原始数据,而不是其副本。
(1)、创建: int rats ; int & a = rats;
(2)、引用必须在声明时初始化,不能先声明再初始化。也不能通过赋值来设置引用。
(3)、引用一旦与某个变量关联起来,就一直效忠。
(4)、若引用参数是const,若实参类型正确却不是左值 或 类型不正确却可以转换成正确类型 时,将创建临时变量。
(5)、返回引用时,应避免返回函数终止时不再存在的内存单元引用。
22、左值——
(1)、可被引用的数据对象。如变量、数组元素、结构成员、引用和解除引用的指针等。
(2)、非左值,包括字面常量和包含多项的表达式。
(3)、常规变量属于可修改的左值,const变量属于不可修改的左值。
23、右值引用——可指向右值的引用,使用&&声明。如: double & rref = std::sqrt ( 26.00 ) ;
24、默认参数——通过函数原型设置函数参数默认值。
(1)、必须从右到左添加默认值。
(2)、实参按照从左到右的顺序依次被赋值给形参,而不能跳过任何参数。
25、函数重载——参数列表(特征标)不同,而函数名相同的函数。
(1)、类型引用和类型本身被视为同一个特征标。
(2)、不能把const变量赋值给非const形参
26、名称修饰——根据函数原型中指定的形参类型对每个函数名进行加密,用来跟踪每一个重载函数。
27、函数模板——相当于Java中的泛型
(1)、声明: template <typename T> void Swap(T &a, T &b); // typeName 可用 class 替换
(2)、函数模板不能缩短可执行程序,最终的代码不包含任何模板,只包含了为程序生成的实际函数。
(3)、一般将模板放在头文件中。
28、显式具体化——具体化的函数定义,匹配时,使用它而不是模板。
(1)、非模板函数: void swap( job &, job &);
(2)、模板函数: template <typeName T> void swap( T & ,T &);
(3)、显式具体化: template<> void swap<job>( job &, job &); // swap<job>中job是可选的
(4)、编译器在选择原型时: 非模板函数 > 显式具体化 > 模板函数
(5)、显式实例化: template void swap<int> ( int, int); // template后无<>
(6)、隐式实例化: 对于模板函数,编译器会通过对这个模板含数的引用生成一个含数的实例,这通常叫隐式实例化
29、decltype——decltype( expression ) var; // 让var的类型与expression一样。
(注意!若expression是一个函数调用,var的类型与其返回值相同。若expression是一个左值,var为指向其类型的引用)
30、后置返回类型——给函数指定返回类型。
如:template<class T1, class T2> auto gt( T1 x, T2 y) -> decltype( x + y ) { ... return x + y ;}