基本的异常处理

异常处理机制:暂缓问题处理,不在当前函数中处理,在他的调用者中处理(先上车,后补票)

什么是异常:任何东西都可以认为是异常,错误只是异常的一种

异常一旦被抛出,不做处理,如果引发异常,会调用默认abort函数终止程序

捕获和处理异常:

throw 抛出异常: (值是任何类型都可以,只是我们处理异常的一个参照,类似返回值)

try(检查,捕获)和catch(处理异常): 必须是一起出现,并且它们的括号{ }不能省略

tip:   任何东西都可以认为是异常,错误只是异常中的一种---出现一种情况不能让程序正常运行

怎么抛出异常

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

//求a和b的余数    怎么抛出异常

int division(int a,int b){

    if(b==0)

        throw 0;    //抛出一个值(任意)--->之后处理

    return a/b;

}

void print(int a,int b){

  

    cout<<division(a,b);

}

int main(){

  

    print(1,0);

  

}

/* 把b==0 的情况称为异常,b==0 时代码不成立,会调用默认abort函数终止程序 */

异常一旦被抛出,不做处理,如果引发异常,会调用默认abort函数终止程序 

捕获和处理异常

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

//try 与catch必须是一起出现,并且他们的括号{}不能省略

try

{

    //正常需要检查是否存在异常的代码

}

catch(类型)     //理解为switch中case语句

{

    //处理是根据抛出数据类型决定如何处理 匹配项 匹配抛出异常的类型

}

  

//一个try可以对应多个catch

try

{

    //...   

}

catch(int)

{

         

}

catch(double)

{

     

}

catch(string)

{

         

}

//catch和if else_if 执行机制是一样的,只能执行一个匹配项

小知识:

  • 对try{ }   catch(){ } 的理解:在当前位置引发异常,直接从这个位置跳到catch的位置执行catch的代码 --- 类似switch case 语句
  • catch中int和char不会做转换
  • 写两个相同的类型不被允许,哪段代码先引发异常就先调用catch

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

int division(int a,int b){

    if(b==0)

        throw 0;   

    return a/b;

}

void print(int a,int b){

  

    cout<<division(a,b);

}

int main(){

  

    try

    {

        print(1,0);     //检查异常

        cout<<"别的代码"<<endl;    //这一句不会运行,会直接跳到catch

    }   

    catch(int)                    //抛出的是int类型,捕获int类型

    {

        cout<<"除数不能为0"<<endl;

    }

}

程序能抛出(存在)多个异常,但是只能同时处理1个异常,不能同时引发多个异常

不存在异常的描述 --- 标识性作用    

  • throw ()
  • noexcept

1

2

3

4

5

6

7

8

9

10

//某个函数不存在异常,在某个函数后面用throw() 描述,表示它不存在异常

void  print() throw()

{

    cout << "当前函数不存在抛出异常操作" << endl;

}

void printData() noexcept

{

    cout << "c++新标准中的关键字: 不存在抛出异常操作" << endl;

    //throw 0;  一旦说明没有异常操作,就不能抛出

}

删减符 ...

任何类型的异常都捕获    不管抛出啥,在哪里抛出的,只要引发异常都可以捕获到

1

2

3

4

5

catch(...)

{

  

    cout <<"捕获任何类型的异常"<< endl;

}

异常处理中的传参操作  --- 可以写一个变量进去

catch(int a)/* 隐藏了一个传参操作 可以传任何类型,包括自定义类型都可以 */

注意c++中string的处理    /* string类型与const char* 类型区别 */

代码解析:

对  通过抛出字符串,隐藏了一个传参操作  的理解

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

int divisor(int a, int b)

{

    if (b == 0)

        throw string("除数不能为0");   

    return a / b;

}

  

int main()

{

    try

    {

        divisor(1, 0);  

    }

    catch (string str)    //把throw的内容赋值给str    str="除数不能为0"

    {

        cout << str << endl;

    }

}

注意string类型与const char* 类型区别  --- 出现类型不匹配,c++对传参类型要求更严格

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

int divisor(int a, int b)

{

    if (b == 0)

        throw "除数不能为0";    //抛出异常  解析为char* 类型 写catch时不能直接当作string

    if(b==1)

        throw "除数不能为1";    /* 不同问题的抛出,不能用固定类型(int、char...), 可以选择

                                  抛出不同字符串处理 string1,string2,string3... 通过传

                                  参的方式去描述问题 */

    if(b==2)

        throw string("除数不能为2");  //需构造无名参数作捕获对象处理--->需要自己触发

    return a / b;

}

  

int main()

{

    try

    {

        divisor(1, 0);       //直接触发异常

    }

    catch (const char* str)  //抛出的是char* 类型,不能当作string

    {

        cout << str << endl;

    }

    try

    {

        divisor(1, 2);

    }

    catch (string str)    //如果要捕获string类型,需要自己构造一个string对象返回

    {

        cout << str << endl;    //直接输出str

    }

}

可以抛出自己类的对象

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

class Error

{

public:

    Error(const char* str = "未知错误") :str(str) {}

    const char* what()const

    {

        return str.c_str();

    }

protected:

    string str;

};

  

void insertArray(int array[], int* curNum, int posData,int maxLength)

{

    if (*curNum >= maxLength)  //3>=3

    {

        throw  Error("数组下标溢出!");

    }

    //0 1 2

    array[*curNum] = posData;  //array[3]=3

    (*curNum)++;

}

  

int main(){

  

    try

    {

        int array[3] = { 0,0,0 };

        int curNum = 0;

        for (int i = 0; i < 4; i++)

        {

            insertArray(array, &curNum, i, 3);

        }

    }

    catch (Error str)

    {

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

    }

  

    return 0;

}

标准库当中的异常类

1

#include<exception>    //父类(基类)

子类很多,子类描述的问题不同而已

例子: const char* _ptr; 一个数据成员,用于描述标准库当中异常的字符串,用字符指针存放那个字符串

what( )方法  用于返回数据成员的        1.虚函数        2.不存在异常

return _ptr ? _ptr : "unknow";判断char* 类型的指针是不是为空,不等于空,返回你描述的错误,等于空(由于没有传参),返回未知错误"unknow"

引发标准库中内存申请失败的异常

发现代码出现abort( )错误,可以通过这种方式找到,这里是针对内存申请失败做了单一处理,如果不做处理,会直接调用abort函数终止程序

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

#include <exception>

#include <iostream>

using namespace std;

class Exception

{

public:

    Exception(const char* ptr="UNKNOW") :ptr(const_cast<char*>(ptr)){} /*构造函数 干掉           

                                                                         常属性*/

    virtual const char* what() const    //父类是虚函数 且不存在异常

    {

        return ptr;

    }

protected:

    char* ptr;

};

//子类继承父类   

class Bad_alloc :public Exception

{

public:

    Bad_alloc(const char* _Message = "bad exception") :Exception(_Message) {} /*调用父类

                                                           的构造函数抛出bad exception*/

protected:

};

//子类继承父类    调用父类构造函数

class Run_time :public Exception

{

public:

    Run_time(const char* _Message = "run_time error") :Exception(_Message) {}

protected:

};

  

int main()

{

    try

    {

        while (1)

        {

            int* p = new int[1024*1024*10];//一直做内存申请,不做释放,最后一定会内存申请失败

        }

    }

    catch (bad_alloc& object) /* 内存申请失败,调用bad_alloc 标准库中的异常,创建一个对象接收一           

                                 下,子类中的what()方法调用父类中的what()方法打印 */

    {

  

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

    }

    return 0;

}

/*输出*/

  

bad allocation    //调用时抛出 bad allocation 是子类对象调用继承下来的what()方法

  

//一般写代码出现莫名的中断,原因是不做异常处理,引发了abort函数中断程序,一般这种错误都是特殊错误

标准库中传两个参数起到标识作用,由于:引发了不同的错误,不同错误对应了不同的错误编码 对这些错误有特定的描述 ---> 工具 ---> 错误查找 ---> 输入错误编码

值:3

错误信息:系统找不到指定的路径 

更多推荐