C++ PRIMER 学习笔记 第六章

Posted on 2022-08-10 10:35  金色的省略号  阅读(24)  评论(0编辑  收藏  举报

第六章 语句

  语句类似于自然语言的句子,C++语言既有只完成单一任务的 简单语句,也有作为一个单元执行的由一组语句组成的 复合语句,提供了实现条件分支结构  以及 重复地执行同一段代码的循环结构的语句通常情况下语句 是顺序执行的,C++定义了一组 控制流语句(if和switch语句,for、while、do while语句),允许有条件地执行 或 重复地执行 某部分功能

  6.1 简单语句

  C++中,大多数语句以分号结束(块不是以分号结束的)
  表达式语句,表达式加上分号,就是表达式语句
  空语句,只有一个单独的分号,如果在程序的某个地方,语法上需要一个语句,但逻辑上并不需要,此时应该使用空语句,这种用法常见于在循环条件判断部分就能完成全部循环工作的情况

  6.2 声明语句

  在C++中,对象或类的定义或声明也是语句,定义语句经常被称为声明语句(declaration statement) 

  6.3 复合语句(块)

  复合语句(compound statement),通常被称为块,是用一对花括号括起来的语句序列(也可以为空),块标识了一个作用域,与其他大多数语句不同,块并不是以分号结束的,复合语句用在语法规则要求使用单个语句,但程序逻辑却需要不止一个语句的地方

  6.4 语句作用域

  有些语句允许在它们的控制结构中定义变量在条件表达式中定义的变量必须初始化该条件检验的就是初始化对象的值
  在语句的控制结构中定义的变量,仅在定义它们的块语句结束前有效,如果程序需要访问某个控制结构中的变量,那么这个变量必须在控制语句外部定义

  6.5 if语句

  if语句根据特定表达式是否为真有条件地执行另一个语句,if语句有两种形式:其中一种带else分支而另一种则没有,如果在条件表达式中定义了变量,那么变量必须初始化,将初始化的变量转换为bool值,该bool值决定条件是否成立,变量类型可以是算术类型或指针类型,一个类类型是否用在条件表达式中取决于类本身(IO类型可以用作条件,但vector类型和string类型一般不可用作条件)

  可以通过花括号将内层的if语句括起来成为复合语句,从而迫使这个else子句与外层的if匹配,来解决悬垂else问题

  6.6 switch语句

  switch语句提供了一种更方便的方法来实现深层嵌套的if/else逻辑,通过圆括号内表达式的值与其后列出的关键字做比较,实现switch语句的功能,表达式必须产生一个整数结果,其值与每个case的值比较,关键字case和它所关联的值称为case标号,每个case标号的值都必须是一个常量表达式,除此之外,还有一个特殊的case标号--default标号,default标号提供了相当于else子句的功能,一个标号不能独立存在,它必须位于语句之前,如果switch结构以default标号结束,而且default分支不需要完成任何任务,那么该标号后面必须有一个空语句

  程序执行匹配的 case标号相关联的语句,从该点开始执行,并跨越case边界继续执行其他语句,直到switch结束或遇到break语句为止

  switch求解的表达式可以非常复杂,特别是,该表达式也可以定义和初始化一个变量,变量始终存在于整个switch语句中,在switch结构外面该变量就不再有效

  对于switch结构,只能在它的最后一个case标号或default标号后面定义变量,制定整个规则是为了避免出现代码跳过变量的定义和初始化的情况,如果需要为某个特殊的case定义变量,则可以引入块语句,在该块语句中定义变量,从而保证这个变量在使用前被定义和初始化

  6.7 while语句

  当条件为真时,while语句反复执行目标语句,循环条件不能为空,可以是一个表达式,或者是提供初始化的变量定义,在循环条件中定义的任意变量都只在于while关联的块语句中可见,在循环条件中定义的变量在每次循环里都要经历创建和撤销的过程,while循环内的赋值操作是一种常见的用法

  6.8 for循环语句

   for语句头中,可以省略初始化语句、条件语句或者表达式中的任何一个或全部,可以在for语句的初始化语句中定义多个对象,但是不管怎样,该处只能出现一个语句,因此所有的对象必须具有相同的一般类型

  6.9 do while 语句

  与while语句不同,do while语句总是以分号结束,任何在循环条件中引用的变量都必须在do语句之前就已经存在 

  6.10 break语句

   break语句用于结束最近的while do while for switch语句,并将程序的执行权传递给紧接在被终止语句之后的语句

  6.11 continue语句

  continue语句导致最近的循环语句的当次迭代提前结束,对于while和do while语句,继续求解循环条件,对于for循环,程序流程接着求解for语句头中的expresion表达式

  6.12 goto语句

  goto语句提供了函数内部的无条件跳转,实现从goto语句跳转到同一函数内某个带标号的语句,goto语句的语法规则如下:goto label;,在任何语句前提供一个标识符和冒号,即带标号的语句,形成标号的标识符只能用作goto的目标

  goto语句和获得所转移的控制权的带标号的语句必须位于同一个函数内goto语句不能跨越变量的定义语句向前跳转,如果确实需要在goto和其跳转对应的标号之前定义变量,则定义必须放在一个块语句中

  6.13 try块和异常处理

  异常就是运行时出现的不正常,存在于程序的正常功能之外,并要求程序立即处理
  异常机制 提供程序中 错误检测与错误处理 部分之间的通讯C++的异常处理 包括:throw表达式(throw expression),错误检测部分使用这种表达式来说明遇到了不可处理的错误,可以说throw引发了异常条件,try块(try block),错误处理部分使用它来处理异常,try语句块以try关键字开始,并以一个或多个catch子句结束,由标准库定义的一组异常类(exception class)用来在throw和相应catch之间传递有关的错误信息

    6.13.1 throw表达式

  系统通过throw表达式抛出异常,throw表达式由关键字throw以及尾随的表达式组成,通常以分号结束,这样它就成了表达式语句,throw表达式的类型决定了所抛出异常的类型,runtime_error类型是标准库异常类中的一种,在stdexcept头文件中定义

    6.13.2 try块

  try块以关键字try开始,后面是花括号括起来的语句序列块try块后面是一个或多个catch子句,每个catch子句包括三个部分:关键字catch,圆括号内单个类型或单个对象的声明,称为 异常说明符,以及通常用花括号括起来的语句块,如果选择了一个catch子句来处理异常,则执行相关的块语句,一旦catch子句执行结束,程序流程立即继续执行紧随着最后一个catch子句的语句,try块里面可以包含任意C++语句,包括变量声明

    try{    
        throw runtime_error("There\n" );
    }catch(runtime_error err ){    
        cout << err.what() << "Here\n";
    }

  每一个标准库异常类都定义了名为what的成员函数,这个函数不需要参数,返回c风格字符串,what返回的c风格字符串,是用于初始化runtime_error的string对象的副本

  寻找处理代码的过程与函数调用链刚好相反,抛出一个异常时,首先要搜索的是抛出异常的函数,如果没有找到匹配的catch,则终止这个函数的执行,并在调用这个函数的函数中寻找相匹配的catch,如果仍然没有找到相应的处理代码,该函数同样要终止,搜索调用它的函数,如此类推,继续按执行路径回退,直到找到适当类型的catch为止,如果不存在处理该异常的catch子句,程序的运行就要跳转到名为terminate的标准库函数,该函数在exception头文件中定义,通常情况下,它的执行将导致程序非正常退出

    6.13.3 标准异常

  标准库异常类定义在四个头文件中:exception头文件  定义了最常见的异常类,它的类名是exception; stdexcept头文件  定义了几种常见的异常类;new头文件  定义了bad_alloc异常类型,提供因无法分配内存而由new抛出的异常;typeinfo头文件  定义了bad_cast异常类型

  标准库异常类只提供很少的操作,包括创建、复制异常类型对象以及异常类型对象的赋值,exeception、bad_alloc以及bad_cast类型只定义了默认构造,无法创建这些类型的对象时为它们提供初值,其他的异常类型则只定义了一个使用string初始化式的构造函数,当需要定义这些异常类型的对象时,必须提供一个string参数,异常类型只定义了一个名为what的操作,这个函数不需要任何参数,并且返回const char*类型的值

/*     #include <stdexcept>
    #include <new>
    #include <typeinfo> */
    
    try{    
        throw// exception();
            //bad_alloc();
            //bad_cast();
            //logic_error("found_logic_error" );
            "const char* ";
    }/* catch(bad_alloc ba){
        cout << ba.what();
    }
    catch(bad_cast bc){
        cout << bc.what();
    } */
    catch(const char* cc){
        cout << cc;
    }
    catch(exception& e){
        cout << e.what();
    }
View Code

  6.14 使用预处理器进行调试

   程序所包含的调试代码仅在开发过程中执行,当应用程序已经完成,并且准备提交时,就会将调式代码关闭,可使用 NDEBUG 预处理变量  实现有条件的调试代码,开发完成后,要将程序交付给客户时,可通过定义NDEBUG预处理变量,删除这些调试语句
#include <iostream>
using namespace std;

int main()
{    
    //#define NDEBUG //关闭
    #ifndef NDEBUG
    cerr << "main starting" << endl;
    #endif
    
    return 0; 
}
View Code
  预处理器还定义了其余四种在调试时非常有用的常量:__FILE__  文件名,__LINE__   当前行号, __TIME__  文件被编译的时间,  __DATE__  文件被编译的日期
  调试技术 使用NDEBUG预处理变量以及assert断言预处理宏,assert宏是在cassert头文件中定义的,预处理宏有点像函数调用,assert宏需要一个表达式作为它的条件:assert(expr),与异常不同,程序员使用assert来测试不可能发生的条件
#include <cassert>
#include <iostream>
using namespace std;

int main()
{    
    //#define NDEBUG //关闭
    #ifndef NDEBUG
    assert(1>2 );  //表达式 1>2 "不可能发生的事情" 会输出信息并终止程序的执行
                   //如果表达式非零值,则assert不做任何操作
    #endif
    
    return 0; 
}
View Code