铅笔

在你的害怕中坚持的越多,你就会越自信
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

异常类之基类

Posted on 2018-08-28 00:46  黑色の铅笔  阅读(1842)  评论(0编辑  收藏  举报

1.C++中的异常

C++异常处理语法元素try-catch语句
  • try语句处理正常代码逻辑  (但有可能产生异常,产生异常时抛出异常并转到catch语句块里面的代码)
  • catch语句处理异常情况
  • try语句中的异常由对应的catch语句处理
  • C++通过throw语句抛出异常信息(一般在try语句中)

 

throw和return区别
throw异常的返回一个值,这个值就代表了异常,   抛到catch语句块 
return正常的返回一个值
 
C++异常处理分析
(1)throw抛出的异常必须catch处理
  • 当前函数能够处理异常(含有catch语句块,程序继续往下执行
  • 当前函数无法处理异常,则函数停止执行并返回,由上层函数进行处理了
(2)未被处理的异常会顺着函数调用栈向上传播,直到被处理为止,否则程序将停止执行。如果一直未处理则会到main函数,该函数中若果仍没有try..catch语句直接退出
 

(3)同一个try语句可以跟上多个catch语句

  ①catch语句可以定义具体处理的异常类型

  ②不同类型的异常由不同的catch语句负责处理

  ③try语句中可以抛出任何类型的异常

  ④catch(…)用于处理所有类型的异常

  ⑤任何异常都只能被捕获(catch)一次

(4)异常处理的匹配规则

 

 

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 double divide(double a, double b)
 6 {
 7     const double delta = 0.000000000000001;
 8     double ret = 0;
 9 
10     if( !((-delta < b) && ( b < delta))){
11         ret = a / b;
12     }else{
13         throw 0; //产生除0异常
14     }
15 
16     return ret;
17 }
18 
19 //匹配规则(自上而下,严格匹配、不进行类型转换)
20 void Demo1()
21 {
22     try{
23         throw 'c';  //抛出字符型的异常
24     }catch(int i){
25         cout << "catch(int i)" << endl;
26     }catch(double d){
27         cout << "catch(double d)" << endl;
28     }catch(char c){ //只能被这里的catch捕获
29         cout << "catch(char c)" << endl;
30     }
31 }
32 
33 void Demo2()
34 {
35     throw "Demo2";   //const char*
36 }
37 int main()
38 {
39     cout << "main() begin" << endl;
40 
41     try{
42         double c = divide(1, 0);//产生异常
43         cout <<"c = " << c << endl; //无法被执行到!
44     }catch(...){
45         cout << "Divide by zero..." << endl;
46     }
47 
48     Demo1();
49 
50     try{
51         Demo2();
52 53       catch(char* c)
54      {
55         cout << "catch(char* c)" << endl;
56     }
57     catch(const char* c)
58         {
59         cout << "catch(const char* c)" << endl;
60     }catch(...)
61      {
62        //写位置最后
63         cout << "catch(...)" << endl;
64     }
65 
66     cout << "main() end" << endl;
67     return 0;
68 }
View Code

 

2.异常类的构建

  • 异常的类型可以是定义类型
  • 对于类类型异常的匹配依旧是至上而下严格匹配(不能强制类型的转换)
  • 赋值兼容性原则(子类的对象可以直接复制给父类的对象,父类的指针可以直接指向子类的对象在异常匹配中依然使用 (出现父类对象是可以用一个子类对象代替)
  • 一般而言:
    ------匹配子类异常的catch放在上部
    ------匹配父类异常的catch放在下部

3、异常类族

现代C++库必然包含充要的异常类族 
异常类族是数据结构类所以来的“基础设施
 
 顶层父类Exception是一个抽象类,不能定义对象,用于被继承的。
这一节就是实现这个抽象基类的。
目的:当程序抛出异常时(有可能会是一个字符串、文件名、行号),能够捕捉到这个异常,并打印处异常的信息等。
要实现的几个函数:
构造函数:对收到的异常进行初始化工作
1     Exception(const char* message); 
2     Exception(const char* file,int line);
3     Exception(const char* message,const char* file,int line);

由于初始化工作差不多,为了代码整洁我们将相同的操作封装成了一个函数即:

void  init(const char*,const char*,int);//由于三个构造函数中的逻辑很相似,所以可以将相似的部分统一放到一个函数init()

此外还需要保存一些初始化变量的成员变量

1 char* m_message;//m_message指向一个字符串,用于说明当前的异常信息
2 char* m_location;//m_location指向一个字符串,用于说明当前的异常位置

由于涉及到堆空间即需进行深拷贝,就不能使用默认的拷贝构造函数和赋值操作符,需要自己定义实现即

1 //涉及到堆空间即需进行深拷贝,拷贝构造函数和"="
2     Exception(const Exception& e);
3     Exception& operator =(const Exception& e);

 

 

 

完整代码如下

Exception.h 

 1 #ifndef _EXCEPTION_H_
 2 #define _EXCEPTION_H_ 
 3 namespace DataStructureLib
 4 {
 5 
 6 class Exception
 7 {
 8 protected:
 9     char*  m_message;
10     char*  m_location;
11 protected:
12     void  init(const char*,const char*,int);//由于三个构造函数中的逻辑很相似,所以可以将相似的部分统一放到一个函数init()
13 public:
14     Exception(const char* message); 
15     Exception(const char* file,int line);
16     Exception(const char* message,const char* file,int line);
17 
18     //涉及到堆空间即需进行深拷贝,拷贝构造函数和"="
19     Exception(const Exception& e);
20     Exception& operator =(const Exception& e);
21 
22     virtual const char* message() const;
23     virtual const char* location() const;
24 
25     virtual ~Exception(void);
26 };
27 #endif
28 }

Exception.cpp 

 1 #include "Exception.h"
 2 #include <cstring>
 3 #include <cstdlib>
 4 
 5 namespace DataStructureLib
 6 {
 7 void Exception::init(const char* message,const char* file,int line)
 8 {
 9     m_message=strdup(message);//这里不能直接使用m_message=message,
10                             //因为message指针指向的数据有可能会在栈、堆、全局区,如果是在栈上,局部变量可能会消失,如果直接使用m_message=message就会不安全    
11     if(file!=NULL)
12     {
13         char sl[16]={0};
14         itoa(line,sl,10);//将line转为char类型 10代表十进制
15 
16         m_location=static_cast<char* >(malloc(strlen(file)+strlen(sl)+2));//加2表示后面的":"和结束符即“/0”
17         m_location=strcpy(m_location,file);
18         m_location=strcat(m_location,":");
19         m_location=strcat(m_location,sl);
20     }
21 }
22 
23 Exception::    Exception(const char* message)
24 {
25     init(message,NULL,0);
26 }
27 
28 Exception::Exception(const char* file,int line)
29 {
30     init(NULL,file,line);
31 }
32 
33 Exception::Exception(const char* message,const char* file,int line)
34 {
35     init(message,file,line);
36 }
37 
38 Exception::~Exception(void)
39 {
40     
41     free(m_message);
42     free(m_location);
43 }
44 
45 const char* Exception::message() const
46 {
47     return m_message;
48 }
49 
50 const char* Exception::location() const
51 {
52     return m_location;
53 }
54 
55 Exception::Exception(const Exception& e)
56 {
57     m_message=strdup(e.m_message);
58     m_location=strdup(e.m_location);
59 }
60 
61 Exception& Exception::operator =(const Exception& e)
62 {
63     if (this!=&e)
64     {
65         free(m_message);
66         free(m_location);
67 
68         m_message=strdup(e.m_message);
69         m_location=strdup(e.m_location);
70     }
71     return *this;
72 }
73 
74 }

测试:

#include<iostream>

#include "SmartPointer.h"
#include "Exception.h"

using namespace std;
using namespace DataStructureLib;

int main(int argc,char* argv[])
{    
    try
    {
        throw(Exception("test",__FILE__,__LINE__));
    }
    catch (const Exception& e)
    {
        cout<<e.message()<<endl;
        cout<<e.location()<<endl;
    }

    return 0;
} 

 

 

结果: