【C++ primer】第5章 语句


Part I: The Basics
Chapter 5. Statements

语句


switch 语句和 goto 语句注意事项

switch 语句

// initialize counters for each vowel 
unsigned aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0; 
char ch; 
while (cin >> ch) {
	// if ch is a vowel, increment the appropriate counter
	switch (ch) {
		case 'a':
			++aCnt;
			break;
		case 'e':
			++eCnt;
			break;
		case 'i':
			++iCnt;
			break;
		case 'o':
			++oCnt;
			break;
		case 'u':
			++uCnt;
			break;
		default:
			break; 
	}
} 
// print results
cout << "Number of vowel a: \t" << aCnt << '\n'
     << "Number of vowel e: \t" << eCnt << '\n'
     << "Number of vowel i: \t" << iCnt << '\n'
     << "Number of vowel o: \t" << oCnt << '\n'
     << "Number of vowel u: \t" << uCnt << endl;

switch 内部的变量定义
如果被略过的代码中含有变量的定义怎么办?从作用域外的带有初始值的变量的地方,跳到作用域内该变量的地方,是非法的。

case true:
	// this switch statement is illegal because these initializations might be bypassed
	string file_name; // error: control bypasses an implicitly initialized variable
	int ival = 0;     // error: control bypasses an explicitly initialized variable
	int jval;         // ok: because jval is not initialized
	break;
case false:
	// ok: jval is in scope but is uninitialized
	jval = next_num(); // ok: assign a value to jval
	if (file_name.empty()) // file_name is in scope but wasn't initialized
		// ...

如果需要为特定 case 定义和初始化变量,可以通过在块内定义变量来实现,从而确保该变量在任何后续标签处都在作用域之外。

case true:    
	{       
		// ok: declaration statement within a statement block       
		string file_name = get_file_name();       
		// ...    
	}
	break; 
case false:
	if (file_name.empty())  // error: file_name is not in scope

goto 语句

end: return;  // labeled statement; may be the target of a goto

goto 语句无法将控制权,从作用域外的初始化变量的地点,转移到作用域内该变量的地点:

    // . . .    
    goto end;
    int ix = 10; // error: goto bypasses an initialized variable definition
end:
	// error: code here could use ix but the goto bypassed its declaration
	ix = 42;

向后跳过已经执行的定义是可以的。

// backward jump over an initialized variable definition is okay
begin:
	int sz = get_size();
	if (sz <= 0) {
		goto begin;
	}

在上面代码中,goto 语句执行后将销毁 sz。


try 语句块和异常处理

throw 表达式

Sales_item item1, item2; 
cin >> item1 >> item2; 
// first check that the data are for the same item 
if (item1.isbn() != item2.isbn())
	throw runtime_error("Data must refer to same ISBN"); 
// if we're still here, the ISBNs are the same 
cout << item1 + item2 << endl;

类型 runtime_error 是标准库异常类型之一,定义在 stdexcept 头文件中。必须初始化 runtime_error,可通过给它一个 string 对象或 C风格字符串来实现。该字符串提供有关该问题的辅助信息。

try 语句块

通用语法形式:

try {
	program-statements 
} catch (exception-declaration) {
	handler-statements 
} catch (exception-declaration) {
	handler-statements 
} // . . .

编写处理代码

while (cin >> item1 >> item2) { 
	try {
        // execute code that will add the two Sales_items 
        // if the addition fails, the code throws a runtime_error exception 
    } catch (runtime_error err) { 
		// remind the user that the ISBNs must match and prompt for another pair
		cout << err.what() << "\nTry Again?  Enter y or n" << endl;
		char c;
		cin >> c;
		if (!cin || c == 'n')
			break;      // break out of the while loop    
	} 
}

每个库异常类都定义一个名为 what 的成员函数。这些函数没有参数,返回 C风格字符串(即 const char*)。runtime_error 的 what 成员返回用于初始化特定对象的 string 对象副本。如果上一节中编写的代码抛出异常,那么 catch 子句输出:

Data must refer to same ISBN 
Try Again?  Enter y or n

函数在查找处理程序的过程中推出
在复杂的系统中,程序在遇到引发异常的代码之前,其执行路径可能经过多个 try 语句块。例如,try 语句块可能会调用包含 try 的函数,新的 try 语句块又调用另一个包含 try 语句块函数,依此类推。
查找处理程序与函数调用链相反。引发异常时,首先查找引发异常的函数。若找不到匹配的 catch 子句,则该函数终止。接下来查找调用引发异常的函数的函数。如果未找到处理程序,则该函数也会退出。依此类推,沿着执行路径倒退,直到找到适当类型的 catch 子句为止。
如果最终没有找到任何匹配的 catch 子句,程序转到名为 terminate 库函数。
如果一段程序没有 try 语句块且引发异常,系统会调用 terminate 函数,终止程序。

void throwExec()
{
	try {
		cout << "inner..." << endl;
		throw runtime_error("error!");
	}
	catch(runtime_error e){
		cout << "inner: " << e.what() << endl;
	}
}

int main()
{
	try {
		cout << "outer..." << endl;
		throwExec();
	}
	catch (exception e) {
		cout << "outer: " << e.what() << endl;
	}
}

运行结果如下:

outer...
inner...
inner: error!

——————————

void throwExec2()
{
	try {
		cout << "inner2..." << endl;
		throw runtime_error("error!");
	}
	catch(overflow_error e){
		cout << "inner2: " << e.what() << endl;
	}
}

int main()
{
	try {
		cout << "outer..." << endl;
		throwExec2();
	}
	catch (exception e) {
		cout << "outer: " << e.what() << endl;
	}
}

运行结果如下:

outer...
inner2...
outer: error!

注意:编写异常安全(exception safe)代码非常困难
异常中断了程序的正常流程。
通常,异常会导致处理对象处于无效或未完成的状态,或者资源没有正常释放等。
异常安全代码:在异常发生期间正确执行了“清理”工作的程序。
有些程序仅在异常情况发生时使用异常来终止程序。这样的程序通常不用担心异常安全性。
通常,处理异常并继续执行的程序,必须始终知道,是否可能发生异常,以及该程序必须执行哪些操作以确保对象有效、资源不泄漏与程序恢复到适当的状态。

标准异常

C ++库定义了几个类,用于报告标准库中的函数遇到的问题。这些异常类也可以在用户编写的程序中使用。这些类分别定义在四个头文件中:

  • exception 头文件定义了最通用的异常类 exception。它仅传达异常发生,但不提供其他信息。
  • stdexcept 头文件定义了几种通用异常类,这些异常类在表5.1中列出。
  • new 头文件定义了 bad_alloc 异常类型。
  • type_info 头文件定义了 bad_cast 异常类型。

表5.1 定义在 <stdexcept> 中的异常类

解释
exception最普遍的问题
runtime_error只有在运行时才能检测出的问题
range_error运行时错误:生成的结果超出了有意义的值域范围
overflow_error运行时错误:计算上溢
underflow_error运行时错误:计算下溢
logic_error程序逻辑错误
domain_error逻辑错误:参数对应的结果值不存在
invalid_argument逻辑错误:错误参数
length_error逻辑错误:试图创建一个超出该类型最大长度的对象
out_of_range逻辑错误:使用一个超出有效范围的值

库异常类只有几个操作。可以创建,复制和赋值任何异常类型的对象。
只能默认初始化 exception,bad_alloc 和 bad_cast 对象;无法为这些异常类型的对象提供初始值。
其他异常类型具有相反的行为:可以使用 string 对象或 C风格字符串初始化那些对象,但无法默认初始化它们。当创建任何这些其他异常类型的对象时,必须提供一个初始值。该初始值用于提供有关发生的错误的其他信息。
异常类型仅定义一个名为 what 的操作。该函数不带任何参数,并返回一个 const char*,它指向 C风格字符串。返回的 C风格字符串的内容取决于异常对象的类型。对于采用字符串初始化程序的类型,what函数返回该字符串。对于其他类型,返回的字符串的值因编译器而异。


【C++ primer】目录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值