zoukankan      html  css  js  c++  java
  • C++ Primer Plus 读书笔记(第6、7章)

    第六章 分支语句和逻辑运算符

    测试条件发生的强制类型转换

    只要是使用到这些关于真假的判断,系统将强制转换成bool型,所以对于一般的值类型这个转换时有系统自动完成的,然而对于我们自定义的类类型或者是结构就需要通过重载bool强制类型转换运算符来实现这个功能。所以直接将cin>>num放置到if判定中也是可行的,这将会进行一个强制转换来显示上一次的读取是否成功。

     

    条件预算符合错误防范

    variable == value 进行一个反转能够有效的预防错误的发生,因为后者是将一个变量赋值给一个常量,这在编译时就会发生错误。

     

    新的顺序点

    C++规定,||&&运算符均为顺序点,冒号和逗号运算符也是。顺序点就是当遇到顺序点时,所有的副作用都必须要要产生,而对于其他未定义的交给各个编译器自行决定。

     

    其他表示方式

    并不是所有的键盘都提供这些逻辑运算符,所以C++拥有andornot这三个保留字,这意味着这三个单词不能够被表示为变量名等。C语言添加iso646.h即可将其用作运算符。

     

    使用标准库cctype

    虽然我们可以使用 ‘a’<=ch && ch <= ‘z’ 来表示一个输入是否是小写字母,但这依赖于这些字符的ascii码是连续的,且我们不能简单的想象 ’a’<=ch && ch <=’Z’来表示输入是否为一个字母。所以这种比较依赖于编码方式的判断有时候是不靠谱的,因此使用系统必须实现的库函数来解决就简单多了。

    isalnum() 判定是否是字母或者数字

    isalpha() 判定是否为字母

    iscntrl() 判定是否为控制字符

    isdigit() 判定是否为数字

    isgraph() 判定是否除空格之外的打印字符

    islower()  判定是否是小写字母

    isprint() 判定是否是打印字符(包括空格)

    ispunct() 判定是否是标点符号

    isspace() 判定是否是标准空白字符,如空格、进纸、换行符、回车,水平/垂直制表符

    isupper() 判定是否是大写字母

    isxdigit() 判定是否是16进制数字0-9a-fA-F

    tolower() 如果是大写字母则返回其小写,否则返回本身

    toupper() 如果是小写字母则返回其大写,否则返回本身

     

    switch语句的特性

    由于没有自动的break处理,因此可以对统一语句实现多标签。如果既可以使用if...else if语句,也可以使用switch语句的话,那么当选项不少于3个时,应使用switch语句,从代码长度和执行速度而言,switch语句的效率更高。

     

     

     

    第七章 函数——C++的编程模块

     

    要使用C++函数,必须完成如下工作:

    提供函数定义;

    提供函数原型;

    调用函数。

    库函数已经定义并编译好,同时可以使用标准库头文件提供其原型,因此只需要正确地调用这种函数即可。

     

    C++返回值

    一个函数能够返回除了数组类型之外的其他任何类型。

     

    整型的格式控制符

    %d用来读取十进制数,%o用来读取八进制数,%x用来读取十六进制数,%i用来根据格式自动识别并读取。例如:如果使用%d,即时输入010,那么前导0忽略,但是%i则会当做八进制的数处理,十进制输出结果分别是10,8

     

    为什么需要原型

    这样可使得编译器的工作更有效率,否则需要停下当前的工作去寻找某个函数的定义,这样效率将变得很低,而且可能这些定义还有可能不在同一个文件下面。原型的功能如下:

    编译器正确处理函数返回值;

    编译器检查使用的参数数目是否正确;

    编译器检查使用的参数类型是否正确。如果不正确,则转换为正确的类型(如果可能的话)。

    仅当有意义时,原型化才会导致类型转换。例如,原型不会将整数转换为结构或者指针。

    在编译阶段进行的原型化被称为静态类型型检查(static type checking)。可以看出,静态类型检查可捕获许多在运行阶段非常难以捕获的错误。

    关于原型是否要给定一个参数的名字,我们知道原型的参数名仅仅是一个占位符,但是为了更好的理解函数的参数性质,最好在类型相同且不容易弄清楚具体参数时应该给定一个易懂的名字。我们注意到库函数中大部分是使用了类型重定义后的类型名,这确实是一个折中的好办法。

     

    函数参数是传值的

    对于这点我们要注意的在传递数组名时,其实传递的是一个指针(数组名),就像函数并没有办法返回一个数组一样。当然可以将一个数组使用结构体进行封装,那么就能够传递一个数组。

    这里要再次进行的一个说明的是:假如声明int array[3][4]这样一个二维数组。

    数组的地址是&array, 类型是int (*)[3][4],一个指向二维数组的指针,array+1就等于加上了48(以一个int型占4个字节计算),其实也就是通过数值上加上sizeof(*(&array)),理解起来就是当前指针通过一次指针解除的大小,恰好sizeof(array)就是48

    那么array是什么呢,它的类型是int (*)[4],一个指向一维数组的指针,array+1就等于加上了16sizeof(*array)也就是16

    判定一个指针是多少维是根据其需要通过多少次指针解除才能得到最终的基本类型决定的,因此array是一个二维指针,&array是一个三维指针。

     

    函数如何使用指针来处理数组

    当且仅当在函数表示形参时,int *arr 和 int arr[]的意义时是一样的。但是理解起来就稍微有点不同,后者显示的告诉我们传递将是一个数组,而前者可能是一个普通的指向当个值的指针。

    一般情况下,如果要传递一个数组的话,我们只需要传递一个数组名就可以了。当然如果硬是要体现出这个传递的参数的全部的信息,那么可以对这个数组名取一次地址,这样在形参中就要详细的写出所有的维数的信息了。如果这样的话,这个函数的可重复利用率就大大减低了,所以使用两个形参搭配非常好,例如对于一个一维数组而言int array[10]:

    void fun(int arr[], 10) 相比 void fun(int (*arr)[10])的通用性更好。虽然后者更加详细的介绍这个数组的性质,STL中更多的使用数组的开始位置和结束位置来表示一个数组。

    传递数组名的话,我们就只能够控制数组的最高维,其他维在这个数组名中已经隐含了。

     

    const在指针中的应用

    首先说说为什么只在指针中使用const(不牵涉到后面学习到的引用),因为所有的在指针看来的基本类型都是在同样是值传递的过程中,本身就产生了一个副本,也就根本不会影响到实参的值了,所以也就没有必要声明这个变量是只读的。

    对于指针类型就很有必要,加了const不仅能够使得我们的代码在不应该修改值的地方发生错误操作能够在编译阶段就发现(相比在运行阶段去寻找要简单很多),并且能够使得读代码的人明白写代码的人更多心意。

    以前在第四章复合类型讨论过一般的变量是将值作为本身的量,而地址作为派生量。而指针则刚好相反,将地址作为本身的量,而将值作为派生的量(对于指针的地址我们暂时不予考虑)。那么对于const而言,对于值类型来说,地址肯定是const的,因此 const int numint const num是没有区别的。而对于指针类型而言const强调的是这个本身的量还是派生的量呢,定义如下:

    const int * ptr;  // 表示指向常整型的指针变量,const强调的是派生的量

    int * const ptr; // 表示指向整型的常指针变量,const强调的是本身的量

    其实也就是看ptr先与谁结合,先于 结合表示就是一个普通的指针,先与const结合说明是一个常指针。其余部分都是对派生量的描述。

    一条规定:假如涉及的是一级间接关系,则将非const指针赋给const指针是可以的。然而,进入两级间接关系时,与一级间接关系一样将const和非const混合的指针赋值方式将不再安全,如果允许这样做,则可以编写这样的代码:

    const int **pp2;

    int *p1;

    const int n =13;

    pp2 = &p1; // 不允许,但是如果允许的话 

    *pp2 = &n; // 允许,都是const类型

    *p1 = 10; // 允许,但是改变了n的值

    当然造成这种情况的可能还有就是人为的将const型值类型的地址强制转化为非const类型。

     

    函数指针 

    函数指针是一个比较冷僻的知识点,因为平时做题也很少用到,其实每一个函数名就是这个函数的地址,也就是其类型就是一个函数指针,不需要通过取地址操作,它就是个指针。

    声明一个函数指针就是照着某个函数的原型抄一遍,然后将函数名改成(* valname)即可。例如:double fun(int, double) 那么定义指针就是 double (*p_fun)(int, double); 之后就能够完成p_fun = fun; 或者是*p_fun = fun; 这两条语句了,这两条语句也完全等价,只是理解的角度不同,前者认为函数名同样是地址能够直接使用,所有指针也直接用,后者认为既然是指针,当然使用一次解除引用也是理所当然的。

    这里之所以使用括号将p_fun括起来就是应该后面那个括号(写参数列表)的优先级高于前面的 号,因此p_fun先于后面的参数列表结合,函数指针申明就变成了返回一个double指针的函数声明了。

    C++11提供的自动类型推断功能能够很好的解决这类复杂的赋值问题。auto p_fun = fun; 要比前两者简单多了。

    当然typedef也能够解决一定的问题,对于typedef的使用就是定义好一个变量后,在前面加上一个typedef就可以了,以后就可以直接使用这个变量名去定义其他变量了。

  • 相关阅读:
    mysql常用命令
    怎么在cmd中输入mysql就可以进去mysql控制台
    ORA-01659: 无法分配超出 7 的 MINEXTENTS
    Oracle修改表空间自增长
    整理下.net分布式系统架构的思路
    Net分布式系统之一:系统整体框架介绍
    QT QTreeWidget 的一个例子 显示文件(夹)的树形结构
    待读 QT 博客
    如何为 QT5 装上 QT4 才有的库
    Python 获取文件夹里所有 log 的起止时间戳
  • 原文地址:https://www.cnblogs.com/Lyush/p/2915453.html
Copyright © 2011-2022 走看看