0%

异常处理

处理可能出现异常的方法

  • 调用abort()或exit()结束程序
  • 返回错误码
  • 异常机制:1.使用try块执行可能出现异常的代码段 2.throw引发异常 3.catch捕获并处理对应情况

throw机制

若throw引发异常后,匹配对应catch。而后不是将控制权返回调用程序,而是引导程序沿着函数调用序列后退,直到找到try块中的函数
若没有throw出异常,则catch被跳过.
一般而言后退多步,再在main()中寻找与引发异常类型匹配的处理程序作处理。

通常用对象作为异常类型处理

好处是:直接根据类型区分异常,另外对象可以携带信息确定原因,catch根据信息来执行对应的操作

堆栈解退(unwinding the stack)

假设函数出现异常,而不是正确返回而终止。程序释放堆栈中的内存,但不会在释放堆栈的第一个返回地址后停止,而是继续释放堆栈,直到找到位于try块的返回地质。程序进行堆栈解退以回到能够捕获异常的地方时,将释放堆栈中的自动存储型变量。

文件IO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<iostream>
#include <fstream>
using namespace std;

int main()
{
ifstream inFile;
inFile.open("file.txt");
if(!inFile.is_open())
{
cerr <<"Failed open file.txt\n";
exit(0);
}
char ch;
int cnt=0;
while(inFile>>ch)
{
cnt++;
}
cout << cnt <<endl;
inFile.close();
return 0;
}

下面程序为harmony mean的除0错误,抛出const char *类型,匹配输出错误信息的代码。一般抛出错误类型通常为类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <cstdlib>

using namespace std;

double hmean(double a, double b)
{
if (a == -b)
throw "bad hmean() arguments: a = -b not allowed";
return 2.0 * a * b / (a + b);
}
int main()
{
double x, y, z;
cout << "Enter two numbers:";
while (cin >> x >> y)
{
try
{
z = hmean(x, y);
}
catch(const char * s)
{
cout << s << endl;
cout <<"Enter a new pair of numbers: ";
continue;
}

cout << "Harmonic mean of " << x << " and " << y << ". is " << z << endl;
cout << "Enter next set of numbers <q to quit>:";
}
cout << "Bye!" << endl;
return 0;
}

throw-catch机制和函数参数以及函数返回机制的区别

1.函数控制权返回到调用函数的函数,throw语句控制权向上返回到第一个包含能够捕获相应异常的try-catch组合
2.引发异常时,编译器总是创建一个临时拷贝,彻底避免了函数机制指向不存在的临时变量的问题。
3.经常可见catch(problem & p),使用了引用,主要是针对通过继承关联起来的异常类型;此时使用基类引用即可执行派生类对象。此时层次结构最下面的异常类的catch语句应当放在最前面。

catch (…)表示捕获任何异常

异常处理的注意事项

1.使用异常,应在设计程序时就加入,而不是以后才添加。
2.使用异常会增加程序代码,降低程序运行速度。
3.异常规范不适用于模板,异常和动态内存分配不能总是协同工作
因此,虽然异常处理对于某些项目极为重要,但也会增加编程的工作量、增大程序以及降低程序的速度。另外编译器对异常的支持以及用户的经验还没有达到成熟的程度,应有节制地使用这一特性。

如无堆栈解退,则会出现内存泄漏,如下

1
2
3
4
5
6
7
8
9
void test(int n)
{
double * ar = new double[n];
if(oh_no)
throw exception();

delete [] ar;
return ;
}

throw了,则delete[]被忽略,ar指针消失了,内存块未释放且不可访问。

RTTI(RunTime Type Identification)

三个支持RTTI的元素:

  • dynamic_cast操作符将基类指针生成派生指针:回答是否可以安全将对象的地址赋给特定类型的指针?如可,返回对象地址;否则,返回空指针。这对比“指向哪个类型的对象”,这更通用且有用。
  • typeid操作符返回对象的类型
  • type_info存储特定类型的信息
    注意:RTTI只适用于包含虚函数的类

与通用转换机制相比,dynamic_cast、static_cast、const_cast、reinterpret_cast提供了更安全和明确的类型转换。

STL

通用编程技术,迭代器概念。
操作复杂度:固定时间、线性时间、随机时间
线性容器以及联合容器(set、map、multiset、multimap)
()函数符functor的概念,STL使用模板让for_each原型看上去像

1
2
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);

STL算法库

分为四大类

  • 非修改式序列操作
  • 修改式序列操作
  • 排序和相关操作
  • 通用数字运算
    就地算法:在原始数据位置上操作;复制算法:在新的位置操作

IO

cout不会截断数据,会尽量全部打印内容。

1
2
3
4
cout.width(int n); // 单次有效
cout.fill(char c); // 一直有效
cout.precision(int n); // 一直有效