您的位置:首页 > 编程语言 > C语言/C++

[转贴] 从零开始学C++之异常(二):程序错误、异常(语法、抛出、捕获、传播)、栈展开

2013-07-16 19:50 357 查看
一、程序错误

编译错误,即语法错误。程序就无法被生成运行代码。

运行时错误


不可预料的逻辑错误

可以预料的运行异常



例如:




动态分配空间时可能不会成功



打开文件可能会失败



除法运算时分母可能为0



整数相乘可能溢出



数组越界……



二、异常

(一)、异常语法

throw 表达式;

try

{

//try语句块

}

catch(类型1 参数1)

{

//针对类型1的异常处理

}

catch (类型2 参数2)

{

//针对类型2的异常处理

}



catch (类型n 参数n)

{

//针对类型n的异常处理

}

(二)、异常抛出

可以抛出内置类型异常也可以抛出自定义类型异常

throw抛出一个类对象会调用拷贝构造函数

异常发生之前创建的局部对象被销毁,这一过程称为栈展开

(三)、异常捕获

一个异常处理器一般只捕捉一种类型的异常

异常处理器的参数类型和抛出异常的类型相同

…表示可以捕获任何异常

C++ Code
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

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

#include <iostream>

#include <string>

using namespace std;

class MyException

{

public:

MyException(const char *message)

: message_(message)

{

cout << "MyException ..." << endl;

}

MyException(const MyException &other) : message_(other.message_)

{

cout << "Copy MyException ..." << endl;

}

~MyException()

{

cout << "~MyException" << endl;

}

const char *what() const

{

return message_.c_str();

}

private:

string message_;

};

double Divide(double a, double b)

{

if (b == 0.0)

{

MyException e("division by zero");

throw e;

/*throw MyException("division by zero");*/

//throw 1;

}

else

return a / b;

}

int main(void)

{

try

{

cout << Divide(5.0, 0.0) << endl;

}

catch (MyException &e)

{

cout << e.what() << endl;

}

//catch (int)

//{

// cout<<"int exception ..."<<endl;

//}

catch (double)

{

cout << "double exception ..." << endl;

}

catch (...)

{

cout << "catch a exception ..." << endl;

}

}



程序自定义一个异常类型MyException,从输出可以看出,Divide函数内先构造一个MyException对象e,调用构造函数,因为e是局部对象需要被析构,在析构前先调用拷贝构造函数构造另一个对象,这个对象将被catch 引用,最后这个对象在catch末尾也将被析构。

假设没有构造局部对象,直接throw , 如 throw MyException("division by zero"); 那么将不会调用拷贝构造函数,只存在一个对象,在catch的末尾被析构。

假设throw 1; 而没有对应的catch (int) ,即使存在catch (double) 也捕获不到,不会做类型转换,此时会由catch (...) 捕获到,...表示可以捕获任何异常。

(四)、异常传播

1、try块可以嵌套

2、程序按顺序寻找匹配的异常处理器,抛出的异常将被第一个类型符合的异常处理器捕获

如果内层try块后面没有找到合适的异常处理器,该异常向外传播,到外层try块后面的catch块中寻找

3、没有被捕获的异常将调用terminate函数,terminate函数默认调用abort终止程序的执行

可以使用set_terminate函数指定terminate函数在调用abort之前将调用的函数

C++ Code
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

36

37

void MyTerminate()

{

cout << "MyTerminate ..." << endl;

}

int main(void)

{

set_terminate(MyTerminate);

try

{

try

{

throw MyException("test exception");

}

catch (int)

{

cout << "Inner ..." << endl;

cout << "catch a int exception" << endl;

}

//catch (MyException& e)

//{

// cout<<"Inner ..."<<endl;

// cout<<e.what()<<endl;

// throw e;

//}

}

catch (int)

{

cout << "Outer ..." << endl;

cout << "catch a int exception" << endl;

}

catch (MyException &e)

{

cout << "Outer ..." << endl;

cout << e.what() << endl;

}

}



其中MyException类如上,程序中将内层的catch (MyException& e) 屏蔽了,所以由外层的catch (MyException& e) 捕获,假设将两个都注释掉的话,因为没有找到合适的catch, 那么terminate 函数会被调用,并且由于事先set_terminate
函数设定了abort调用之前被调用的函数MyTerminate,故先输出MyTerminate ...然后程序被终止。

三、栈展开

沿着嵌套调用链接向上查找,直至为异常找到一个catch子句。这个过程称之为栈展开。


为局部对象调用析构函数



析构函数应该从不抛出异常




栈展开期间会执行析构函数,在执行析构函数的时候,已经引发的异常但还没处理,如果这个过程中析构函数又抛出新的异常,将会调用标准库的terminate函数。




异常与构造函数




构造函数中可以抛出异常。如果在构造函数函数中抛出异常,则可能该对象只是部分被构造。即使对象只是被部分构造,也要保证销毁已构造的成员。(如果成员是指针p,因为析构函数不会被调用,故不会执行一般的delete p; 很可能造成内存泄漏)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐