码迷,mamicode.com
首页 > 编程语言 > 详细

Standard C++ Episode 6

时间:2015-08-21 07:05:11      阅读:196      评论:0      收藏:0      [点我收藏+]

标签:

 异常
一、为什么要有异常——WHY?
1.通过返回值表达错误
局部对象都能正确的析构
层层判断返回值,流程繁琐
2.采用<setjmp.h>里面定义的setjmp/longjmp远程跳转
一步到位进入错误处理,流程简单
setjmp是给C程序员使用的,根本没有考虑到C++程序员定义的类类型,直接采用setjmp实现跳转会使得某些对象(大部分的局部对象)失去被析构的机会,即使是栈对象。
3.异常处理
局部对象都能正确的析构
一步到位进入错误处理,流程简单
二、异常的语法——WHAT?
1.异常的抛出
throw 异常对象;
异常对象可以是基本类型的变量,也可以是类类型的对象。
当程序执行错误分支时抛出异常。
2.异常的捕获
try {
  可能抛出异常的语句块;
}
catch (异常类型1 异常对象1) {
  处理异常类型1的语句块;
}
catch (异常类型2 异常对象2) {
  处理异常类型2的语句块;
}
...
catch (...) {
  处理其它类型异常的语句块;
}
异常处理的流程:throw 异常对象 之后,立即开始寻找并执行try的右大括号,如果当前函数没有try右大括号则执行函数右大括号。始终沿着函数调用的逆序,依次执行右花括号,直到try 的右花括号,保证所有的局部对象都能被正确地析构,然会根据异常对象的类型,匹配相应的catch分支,进行有针对性的错误处理。
三、异常处理的使用方法——HOW?
1.抛出基本类型的异常,用不同的值代表不同的错误。
2.抛出类类型的异常,用不同的类型表示不同的错误。
3.多采用跑出类类型的异常,通过类类型的异常可以携带更多诊断信息。
4.忽略异常和继续抛出异常。
5.异常说明
在一个函数的形参表后面写如下语句:
...形参表) throw (异常类型1, 异常类型2, ...) { ... } 表示这个函数可以被捕获的异常。
...形参表) throw () - 这个函数所抛出的任何异常均无法捕获。
...形参表) 没有异常说明 - 这个函数所抛出的任何异常均可捕获。
 (p.s. 派生类的虚函数不能抛出比基类被覆盖版本更多的异常类型,举例:
class A {
  virtual void foo (void)
    throw (int, double) { ... }
  virtual void bar (void)
    throw () { ... }
};
class B : public A {
  void foo (void)
    throw (int, char) { ... }
  // ERROR
  void bar (void) { ... } // ERROR
  void bar (void)
    throw () { ... }
};

)
6.使用标准异常:标准异常库定义了多种异常类

#include <stdexcept>

 1 /*
 2  * 异常练习
 3  */
 4 #include <iostream>
 5 #include <cstdio>
 6 using namespace std;
 7 class A {
 8 public:
 9     A (void) {
10         cout << "A构造" << endl;
11     }
12     ~A (void) {
13         cout << "A析构" << endl;
14     }
15 };
16 void func3 (void) {
17     A a;
18     FILE* fp = fopen ("none", "r");
19     if (! fp) {
20         cout << "throw前" << endl;
21         throw -1;
22         cout << "throw后" << endl;
23     }
24     cout << "文件打开成功!" << endl;
25     // ...
26     fclose (fp);
27 }
28 void func2 (void) {
29     A a;
30     cout << "func3()前" << endl;
31     func3 ();
32     cout << "func3()后" << endl;
33     // ...
34 }
35 void func1 (void) {
36     A a;
37     cout << "func2()前" << endl;
38     func2 ();
39     cout << "func2()后" << endl;
40     // ...
41 }
42 int main (void) {
43     try {
44         cout << "func1()前" << endl;
45         func1 ();
46         cout << "func1()后" << endl;
47     }
48     catch (int ex) {
49         if (ex == -1) {
50             cout << "执行失败!改天再见!" << endl;
51             return -1;
52         }
53     }
54     // ...
55     cout << "执行成功!恭喜恭喜!" << endl;
56     return 0;
57 }

 

 

 1 /*
 2  *异常练习
 3  */
 4 #include <iostream>
 5 #include <cstdio>
 6 #include <cstdlib>
 7 using namespace std;
 8 void foo (void) {
 9     FILE* fp = fopen ("none", "r");
10     if (! fp)
11         throw "打开文件失败!";
12     void* pv = malloc (0xFFFFFFFF);
13     if (! pv)
14         throw "内存分配失败!";
15     // ...
16 }
17 int main (void) {
18     try {
19         foo ();
20     }
21     catch (const char* ex) {
22         cout << ex << endl;
23         return -1;
24     }
25     return 0;
26 }

 

 

 1 /*
 2  * 异常练习
 3  * */
 4 #include <iostream>
 5 #include <cstdio>
 6 #include <cstdlib>
 7 using namespace std;
 8 class Error {};
 9 class FileError : public Error {};
10 class MemError : public Error {};
11 void foo (void) {
12     FILE* fp = fopen ("none", "r");
13     if (! fp)
14         throw FileError ();
15     void* pv = malloc (0xFFFFFFFF);
16     if (! pv)
17         throw MemError ();
18     // ...
19 }
20 int main (void) {
21     try {
22         foo ();
23     }
24     catch (FileError& ex) {
25         cout << "打开文件失败!" << endl;
26         return -1;
27     }
28     catch (MemError& ex) {
29         cout << "内存分配失败!" << endl;
30         return -1;
31     }
32     catch (Error& ex) {
33         cout << "一般性错误!" << endl;
34         return -1;
35     }
36     return 0;
37 }

 

 

 1 /*
 2  * 异常处理
 3  * */
 4 #include <iostream>
 5 #include <cstdio>
 6 #include <cstdlib>
 7 using namespace std;
 8 class Error {
 9 public:
10     virtual void print (void) const = 0;
11 };
12 class FileError : public Error {
13 public:
14     FileError (const string& file, int line) :
15         m_file (file), m_line (line) {}
16     void print (void) const {
17         cout << "" << m_file << "文件的第"
18             << m_line << "行,发生了文件错误!"
19             << endl;
20     }
21 private:
22     string m_file;
23     int m_line;
24 };
25 class MemError : public Error {
26 public:
27     void print (void) const {
28         cout << "内存不够啦!!!" << endl;
29     }
30 };
31 void foo (void) {
32     FILE* fp = fopen ("none", "r");
33     if (! fp)
34         throw FileError (__FILE__, __LINE__);
35     void* pv = malloc (0xFFFFFFFF);
36     if (! pv)
37         throw MemError ();
38     // ...
39 }
40 int main (void) {
41     try {
42         foo ();
43     }
44     catch (Error& ex) {
45         ex.print ();
46         return -1;
47     }
48     return 0;
49 }

 

 

 1 /*
 2  *异常练习
 3  */
 4 #include <iostream>
 5 using namespace std;
 6 void foo (void) {
 7     throw 10;
 8 }
 9 void bar (void) {
10     try {
11         foo ();
12     }
13     catch (int& ex) {
14         --ex;
15         throw; // 继续抛出
16     }
17     // ...
18 }
19 int main (void) {
20     try {
21         bar ();
22     }
23     catch (int& ex) {
24         cout << ex << endl; // 9
25     }
26     return 0;
27 }

 

 

 1 /*
 2  *异常练习
 3  */
 4 #include <iostream>
 5 using namespace std;
 6 void foo (void) throw (int, double, const char*) {
 7 //    throw 1;
 8 //    throw 3.14;
 9     throw "Hello, Exception !";
10 }
11 int main (void) {
12     try {
13         foo ();
14     }
15     catch (int ex) {
16         cout << ex << endl;
17     }
18     catch (double ex) {
19         cout << ex << endl;
20     }
21     catch (const char* ex) {
22         cout << ex << endl;
23     }
24     return 0;
25 }

 

 

 

 1 /*
 2  * 异常处理练习
 3  */
 4 #include <iostream>
 5 #include <stdexcept>
 6 #include <cstdio>
 7 using namespace std;
 8 class FileError : public exception {
 9 private:
10     const char* what (void) const throw () {
11         return "文件访问失败!";
12     }
13 };
14 class B {
15 public:
16     B (void) {
17         cout << "B构造" << endl;
18     }
19     ~B (void) {
20         cout << "B析构" << endl;
21     }
22 };
23 class C {
24 public:
25     C (void) {
26         cout << "C构造" << endl;
27     }
28     ~C (void) {
29         cout << "C析构" << endl;
30     }
31 };
32 class A : public C {
33 public:
34     A (void) : m_b (new B) {
35         FILE* fp = fopen ("none", "r");
36         if (! fp) {
37             delete m_b;
38             throw FileError ();
39         }
40         // ...
41         fclose (fp);
42     }
43     ~A (void) {
44         delete m_b;
45     }
46 private:
47     B* m_b;
48 //    C m_c;
49 };
50 int main (void) {
51     try {
52         A a;
53         // ...
54     }
55     catch (exception& ex) {
56         cout << ex.what () << endl;
57         return -1;
58     }
59     return 0;
60 }

 

 

 

四、构造函数中的异常
构造函数可以抛出异常,而且有些时候还必须抛出异常,以通知调用者构造过程中所发生的错误。
构造函数抛出异常后,开始执行try的右大括号,如果没有则执行函数右大括号。
如果在一个对象的构造过程中抛出了异常,那么这个对象就称为不完整对象。不完整对象的析构函数永远不会被执行,但构造函数在未能成功构造完整对象的情况下 会自动回滚(逆序析构已经创建的成员), 但因为回滚不会释放已经动态申请的资源,所以还是需要在throw之前,手动释放动态的资源。
五、析构函数中的异常
析构函数也可以抛出异常,但是会出现操作系统吐核,出现未定义错误。举例:
class A {
    public:
    ~A(void) {
        throw -1;
    }
};

main()
{
    try {
    A a;//构造a
    } //遇到块作用域结束大括号(try右大括号)开始析构a, 析构a时候抛出异常对象,抛出异常对象后最终执行try右大括号,遇到try右大括号开始析构a...这样就死循环了,部分编译器(例如gcc)经过编译 期间处理就避免了死循环,但是部分编译器就没有处理这个问题,最终程序执行可能导致未定义错误,操作系统吐核。
    catch(int & a) {
    }
}

永远不要在析构函数中抛出异常。可以通过try{}catch(...){}来全面拦截所有可能引发的异常,让析构函数没有可能向外抛出异常。例如:
class A {
public:
  ~A (void) {
    //throw -1;
    try {
      sysfunc ();
    }
    catch (...) {}
  }
};
try {
  A a;
  a.foo ();
}
catch (...) { ... }

Standard C++ Episode 6

标签:

原文地址:http://www.cnblogs.com/libig/p/4746704.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!