【c ++ primer 笔记】第5章 语句-LMLPHP

🌔第5章 语句

🌗5.1 简单语句

表达式语句

  • 一个表达式,末尾加上分号就变成了表达式语句
  • 表达式语句的作用是执行表达式并丢弃掉求值结果
ival + 3;    //一条没有实际用处的表达式语句
cout << ival;//一条有用的表达式语句

空语句

  • 空语句只含单独的一个分号

用法:

  • 用在语法上需要一条语句但逻辑上不需要的地方,此时用空语句
  • 当循环的全部工作条件部分就可以完成时使用空语句应该加上注释。
; //空语句
while (cin >> s && s != sought)
    ;//空语句

复合语句(块)

  • 复合语句是指用{}(花括号)括起来的语句和声明的序列,复合语句也被称作
  • 一个块就是一个作用域
  • 空块的作用等价于空语句

用法

  • 语法上需要一条语句,但是逻辑上需要多条语句
while (val <= 10)
{
	sum += val;
	++ val;
}

🌗5.2 语句作用域

  • 可以在 if、switch、while、for 语句的控制结构内定义变量。。但是定义在控制结构当中的变量只能在内部使用一旦语句结束,变量不可以使用
  • 如果其他代码也需要访问控制变量,则变量必须定义在语句的外部。

🌗5.3 条件语句

条件语句分为俩种if语句和 switch 语句。

🌕5.3.1 if 语句

  • if 语句有两种形式,一种有 else 分支,另一种没有。
  • 使用 if 语句最好在所有的 if 和 else 之后都用花括号
  • 悬垂else(dangling else):用来描述在嵌套的if else语句中,如果ifelse多时如何处理的问题。C++使用的方法是else匹配最近没有配对的if

🌕5.3.2 switch 语句

  • switch 语句计算一个整型表达式的值,然后根据这个值从几条执行路径中选择一条。
  • case 关键字和它对应的值一起被称为 case 标签,case 标签必须为整型常量表达式
  • default 也是一种特殊的 case 标签。
switch(ch){
    case 'a': ++aCnt; break;
    case 'b': ++bCnt; break;
}

int ival = 42;
switch(ch){
case 3.14; //错误
case ival; //错误
}

执行问题:

  • 如果 某个 case 标签匹配成功后,程序将从该 case 标签开始顺序执行所有case分支或者遇到break结尾
  • c++ 程序的形式比较自由,case 标签之后不一定要换行。
  • 一条 case 后可以跟多条语句,不必用花括号括起来。
  • 一般在每个 case标签后都有一条 break 语句。如果需要两个或多个值共享同一组操作,可以故意省略掉 break 语句。
unsigned vowlCnt = 0;
switch(ch){
	case 'a':
	case 'b':
	case 'c':
		++ vowelCnt;
		break;
}

switch(ch){
    case 'a': case 'b':
        ++Cnt;
        break;
}

注意:

  • 一般不要省略 case 分支最后的 break 语句。如果没有 break 语句,最好注释一下。
  • 如果没有任何一个 case 标签匹配 switch 表达式的值,就执行 default 分支。
  • 即使不准备在 default 下做任何工作,最好也定义一个 default 标签。
  • 如果要在 case 分支定义并初始化变量,应该定义在块内以约束其作用域

🌗5.4 迭代语句

迭代语句有三种:while语句、for语句(范围for语句)、do while语句

🌕5.4.1 while 语句

  • while 的条件不能为空。条件部分可以是一个表达式或者是一个带初始化的变量声明
  • 定义在 while 条件部分或while循环体内的变量每次迭代都经历从创建到销毁的过程。
  • while 适合不确定到底迭代多少次的情况。
while (condition)
	statement

🌕5.4.2 传统的for语句

for(init-statement; condition; expression)
    statement;
  • init-statement 可以是声明语句表达式语句空语句
  • init-statement 可以定义多个对象,但是只能有一条声明语句,因此所有的变量基础类型必须相同。
  • expression 在每次循环之后执行。
  • for 语句头能省略掉三者中的任意一个或全部。
  • 省略 condition 等于条件恒为 true。
for (int i = 0, j = 0; i < n ;i++)
	statement
for (int i = 0, char j = '0'; i < n ;i++)  //错误

🌕5.4.3 范围for语句

for(declaration : expression)
    statement
  • 范围 for 语句用来遍历容器或其他序列的所有元素。
  • expression 表示的必须是一个序列,可以是花括号括起来的初始值列表。这些序列的共同的是都可以返回迭代器的 begin 和 end 成员。
  • 如果需要对容器中的元素执行写操作,必须将循环变量声明成引用类型
  • declaration 定义一个能从序列中元素转换过来的变量(不是迭代器)。最简单的方法是使用 auto 类型说明符
  • 范围 for 语句不能改变序列的元素数量(增加、删除)。因为范围for预存了 end() 的值,改变元素数量后 end() 的值就可能失效了
  • 每次迭代都会重新定义循环控制变量,并将其初始化为序列的下一个值。

🌕5.4.4 do while 语句

do
	statement
while (condition);
  • do while语句和while语句的唯一区别是do while语句先执行循环体后检查条件。即至少执行一次循环。
  • do while 后不要忘了加分号。
  • 因为 condition 在循环体后面,所以 condition 使用的变量应该定义在循环体外面。

🌗5.5 跳转语句

  • 四种跳转语句:breakcontinuereturngo to

🌕5.5.1 break 语句

  • break 语句负责终止离他最近的whiledo whilefor 或者 switch 语句,并从这些语句之后的第一条语句开始执行。
  • break 语句只能出现在迭代语句或者 switch 语句内部(包括嵌套在此类循环里的语句或块的内部)

🌕5.5.2 continue 语句

  • continue 语句终止最近的循环中的当前迭代并立即开始下一次迭代。
  • continue 只能出现在forwhiledo while中,比break少了一个switch

🌕5.5.3 goto语句

  • goto 语句的作用是从 goto 语句无条件跳转到同一函数内的另一条语句
label : int a = 1;
goto label;
  • label 是用于标识一条语句的标示符
  • 标签标示符独立于变量或其他标示符的名字,标签标示符可以和程序中其他实体的标示符使用同一个名字而不会相互干扰。
  • 如果 goto 语句跳回了一条变量的定义之前意味着系统将会销毁该变量,然后重新创建它。
  • 不要使用 goto,它会令程序又难理解又难修改。

🌗5.6 try 语句块和异常处理

  • 异常是指存在于运行时的反常行为,典型的异常有失去数据库连接和遇到意外输入等。
  • 当程序的某部分检测到一个它无法处理的问题时,需要用到异常处理。此时检测到问题的部分应该发出检测信号。(它不需要处理异常,只需要用于检测异常)
  • 如果程序里有可能引发异常的代码,通常需要有专门的代码处理问题。
  • c++中异常处理机制一般包括异常检测异常处理俩部分协同完成。
  1. throw 表达式:异常检测部分使用 throw 表达式来表示遇到了无法处理的问题。称为 throw 引发了异常。
  2. try 语句块:异常处理部分使用 try 语句块处理异常。try 语句块以关键字 try 开始,并以一个或多个 catch 子句结束。
  3. 一套异常类:用于在 throw 表达式和相关的 catch 子句间传递异常的具体信息。

🌕5.6.1 throw 表达式

  • throw 表达式包含关键字 throw紧随其后的一个表达式,表达式的类型就是抛出的异常类型。
  • throw 表达式后面紧跟一个分号。从而构成一条表达式语句。
  • throw只是用来检测异常,并不处理,处理交给try和catch语句。
  • throw 后面跟一个异常类型的对象(必须同时使用 string 或 C 风格字符串对其初始化)。
if (item1.isbn() != item2.isbn())
	throw runtime_error("Data must refer to same ISBN");
cout << item1 + item2 << endl;

🌕5.6.2 try 语句块

  • try 语句块的一开始是关键字try,随后紧跟一个块。
  • 跟在 try 块后面的是一个或多个 catch 子句。catch 子句包括三部分:关键字 catch括号内一个(可能未命名)的对象的声明(叫做异常声明)一个块
  • try语句块内声明的变量在块外部无法访问,特别是在catch子句也无法访问
  • 当 try 语句块中抛出了一个异常,如果该异常类型与 catch 子句括号中的异常类型相同,就会执行该 catch 子句
  • catch 一旦完成,程序就跳转到 try 语句块最后一个 catch 子句之后的那条语句继续执行。
  • 在 catch 后面的括号里使用省略号(…)可以让 catch 捕获所有类型的异常。
  • 每个标准库异常类都有一个 what 成员函数,它没有参数,返回初始化该对象时所用的 C 风格字符串。
while (cin >> item1 >> item2){}
	try{
	   throw runtime_error("Data must be same as size");//try 语句块抛出了一个异常
	}
	catch(runtime_error err){//在 catch 后面的括号中声明了一个“runtime_error”类型的对象,与 try 抛出的异常类型相同,接下来执行此子句。
		cout << err.what()
		     << "\nTry Again? Enter y or n" << endl;
		char c;
		cin >> c;
		if (!cin || c == 'n')
		{
			break;   //跳出while循环
		}
	}
}

异常运行结果

err.what()就是为了输出上面错误的信息"Data must be same as size"
Data must refer to same ISBN
Try Again? Enter y or n

函数在寻找处理代码的过程中退出

  • throw 语句可能出现在嵌套的 try 语句块中或在 try 语句块中调用的某个函数内。当异常被抛出,首先程序会从内到外一层层寻找相应类型的 catch 子句。如果最后还是没找到,系统会调用 terminate 函数并终止当前程序的执行。
  • 如果对于 throw 语句外就没有 try 语句块,也会执行 terminate 函数

编写异常代码非常困难

  • 异常中断了程序的正常流程。异常发生时,有的程序执行完成有的程序执行到一半就中断了,c此时会导致资源没有正常释放对象处于无效状态等情况。异常安全的代码要求在异常发生时能正确执行清理工作。这个非常困难。

🌕5.6.3 标准异常

C++ 标准库定义了一组异常类,用于报告标准库函数遇到的问题。他们定义在 4 个头文件中

  • 定义在 stdexcept 头文件中的类型必须使用 string 对象或 C 风格字符串来初始化他们。不允许使用默认初始化方式
  • 其他 3 个头文件中的 3 中类型则只能默认初始化,不能提供初始值。
  • 异常类型只有一个 what 成员函数,该函数没有参数,返回是一个 C 风格字符串的指针,目的是提供关于异常的文本信息。
  • 对于无初始值的异常类型,what 返回的内容由编译器决定,有初始值的返回初始值。
  • exception头文件 定义了exception
  • new头文件定义了bad_alloc
  • type_info头文件定义了bad_cast
  • 其余的都定义在stdexcept头文件

上面的异常类之间存在继承关系,其中 exception 是所有其他类的基类,总的继承关系如下图

【c ++ primer 笔记】第5章 语句-LMLPHP

void StrBlob::check(size_type i, const string& msg)
{
	if (i >= data->size())
        throw out_of_range(msg);
}
07-08 07:44