C++ 异常检测

目录

0基础概念 

 1异常捕获语法

1.1基本写法

普通情况 

 Data为自定义类型(类或者结构体)

接受一切throw信息

1.2限定throw类型写法

1.3调用C++标准异常写法

2捕获任意类型 

3异常的嵌套捕获

4对自定义类型异常的捕获

5栈解旋 

6异常的接口声明 

7异常变量的生命周期

8C++标准的异常

9多态


C ++ 异常处理:https://www.runoob.com/cplusplus/cpp-exceptions-handling.html 

0基础概念 

C ++异常是指在程序运行时发生的特殊情况,例如:除0溢出,数组下标越界,所要读取的文件不存在,内存不足等问题

C ++异常处理涉及到三个关键字:try,catch,throw

  • throw:当问题出现时,程序会引发一个异常。这是通过使用throw关键字来完成的。
  • catch:在您想要处理问题的地方,通过异常处理程序捕获异常。
  • 试试: 尝试块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个catch块。

如果有一个块抛出一个异常,捕获异常的方法会使用trycatch关键字。

try块中放置可能引发异常的代码,try块中的代码被称为保护代码。使用try / catch语句的语法如下所示:

头文件:

#include<exception>

基本语法: 

try
{
   // 保护代码
}catch( ExceptionName e1 )
{
   // catch 块
}catch( ExceptionName e2 )
{
   // catch 块
}catch( ExceptionName eN )
{
   // catch 块
}

如果 try 块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个 catch 语句,用于捕获不同类型的异常。

 1异常捕获语法

 C中 处理异常/保护程序 的写法:

1.1基本写法

普通情况 

//throw过程
void Protect_function()
{
     throw Data;
}

//try catch过程
void Except_function()
{
     try
     { 
         Protect_function();//保护程序
     }
     catch(Data_type)
     {
         //输出:qDebug()或者cout<<....
         //如果不想处理,可以写:throw;
     }
}

其中Data的类型是Data_type, 二者必须保持一致。

 Data为自定义类型(类或者结构体)

//throw过程
void Protect_function()
{
     throw Classname();//匿名对象
}

//try catch过程
void Except_function()
{
     try
     { 
         Protect_function();//保护程序
     }
     catch(Classname & e)
     {
         e.MemberFunction;//调用该类中专门的打印错误信息的成员函数
         //输出:qDebug()或者cout<<....
         //如果不想处理,可以写:throw;
     }
}

接受一切throw信息

//throw过程
void Protect_function()
{
     throw whatever;//匿名对象
}

//try catch过程
void Except_function()
{
     try
     { 
         Protect_function();//保护程序
     }
     catch(...)
     {
         //输出:qDebug()或者cout<<....
         //如果不想处理,可以写:throw;
     }
}

catch(...)就是接受一切throw信号 

1.2限定throw类型写法

有时候对throw的类型有限制,什么都不加,就是没有限制,你可以throw任意类型

加上限制的情况如下: 

//throw过程
void Protect_function() throw(DataType1,....,DataTypen)
{
     throw Data;//form DataType1,....,DataTypen
}

//try catch过程
void Except_function()
{
     try
     { 
         Protect_function();//保护程序
     }
     catch(DataTypex)//x = 1,....,n;
     {
         //.........
     }
}

thorw的内容,必须是规定的内容

当然,也可以选择不throw任何的内容

//throw过程
void Protect_function() throw( )
{
     //..........
}

//try catch过程
void Except_function()
{
     try
     { 
         Protect_function();//保护程序
     }
     catch( ) 
     {
         //.........
     }
}

1.3调用C++标准异常写法

         try
         {
             //protect
         }
         catch(系统异常类名 & e)
         {
             cout<<endl<<e.what()<<endl;
         }

 举例说明:

int myDevide(int a,int b)
{
    if(b == 0)
    {
        return -1;
    }
    return a/b;
}
int test01()
{
    int a = 10;
    int b = 0;
    int ret = myDevide(a,b);
    if(ret == -1)
        return 0;
    else
        return ret;
}

以上操作的核心,就是防止程序的崩溃,一旦发现有除0操作,立即返回,防止内存溢出。

C++则用一下方式进行处理:

int myDevide(int a,int b)
{
    if(b == 0)
    {
        throw -1;
    }
    return a/b;
}
int test01()
{
    int a = 10;
    int b = 0;
    try
    {
        myDevide(a,b);
    }
    catch(int)
    {
        qDebug()<<"int异常捕获";
    }
}

throw的是int类型,所以catch括号内的也要是int类型。

如果想捕获任意类型的异常,需要将catch改成如下情况

2捕获任意类型 

    try
    {
        //保护代码
    }
    catch(...)
    {
        qDebug()<<"任何异常捕获";
    }

在异常声明的括号内使用省略号 ...,即可。

3异常的嵌套捕获

catch可以选择处理异常,也可以选择不处理

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    try
    {
        test01();

    }
    catch(...)
    {
        qDebug()<<"构造函数中的异常捕获";
    }
}

MainWindow::~MainWindow()
{
    delete ui;
}
int myDevide(int a,int b)
{
    if(b == 0)
    {
        throw -1;
    }
    return a/b;
}
int test01()
{
    int a = 10;
    int b = 0;
    try
    {
        myDevide(a,b);
    }
    catch(int)
    {
        qDebug()<<"int异常捕获";
    }
    catch(double)
    {
        qDebug()<<"double异常捕获";
    }
    catch(...)
    {
        qDebug()<<"...异常捕获";
    }
}

构造函数里面调用testtest调用myDecidemyDecide出现异常,抛出,给test,test处理,则不说了,test也可以选择不处理,那么就会落到构造函数中的try和catch中进行处理。

以上代码的输出: 

catch中不进行异常处理,直接写throw就行。

int Function_transfer(int a,int b)
{
    if(b == 0)
    {
        throw xxx;
    }
    return qqq;
}
int Function()
{
    int a = 10;
    int b = 0;
    try
    {
        Function_transfer(a,b);
    }
    catch(int)
    {
        throw;//可以选择不处理
        qDebug()<<"int异常捕获";
    }

}

如果所有的catch全部都throw, 那么程序必将崩溃。

                                         

异常抛出(throw)后,必须有人catch,没有,则会调用terminate函数,之后程序崩溃。

同样,也要注意,如果有多个catch,那么catch(...)必须在最后,否则会报错

4对自定义类型异常的捕获

定义一个类:

class myException
{
public:
    void printError()
    {
        qDebug()<<"自定义异常";
    }
};

具体操作:

int myDevide(int a,int b)
{
    if(b == 0)
    {
        throw myException();//匿名对象,抛出对象,不起名字而已
    }
    return a/b;
}
void test01()
{
    int a = 10;
    int b = 0;
    try
    {
        myDevide(a,b);
    }
    catch(int)
    {
        throw;
        qDebug()<<"int异常捕获";
    }
    catch(double)
    {
        qDebug()<<"double异常捕获";
    }
    catch(myException e)
    {
        qDebug()<<"myException异常捕获";
        e.printError();
    }
    catch(...)
    {
        qDebug()<<"...异常捕获";
    }

}

throw的是一个自定义类型的匿名对象,显然,catch要用同样自定义类型的对象,并且在catch后利用这个对象调用这个类中本身自带的打印异常接口。 

5栈解旋 

自定义类如下:

class MyException
{
public:
    MyException()
    {
        qDebug()<<"默认构造函数";
    }
    ~MyException()
    {
        qDebug()<<"析构函数";

    }
    MyException(const MyException &  c)
    {
        qDebug()<<"拷贝构造函数";

    }

};

从try开始,所有栈上的对象,都会被抛出,除了throw的内容,如果也在栈上,那么是在catch完成后抛出,其他内容都是在try开始doWork后抛出。

void doWork()
{
    qDebug()<<"栈解旋部分";
    MyException e1;
    MyException e2;


    throw MyException();
}
void test()
{
    try
    {
        doWork();
    }
    catch(MyException & e)
    {
        qDebug()<<"异常捕获";
    }
}

 

6异常的接口声明 

为了增强函数的可读性,可以在函数声明中列出可能抛出异常的所有类型,如;

void func() throw(A,B,C);

这个函数func能够且只能抛出类型A,B,C以及其子类型的异常。

如果函数声明中没有包含异常接口声明,则此函数可以抛出任何类型的异常,例如:void func()

一个不抛出任何类型异常的函数可声明为:void func() throw()

如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexcepted函数会被调用,然后调用terminate函数中断程序

 先上面举例所述,throw各种各样的数据类型,那么如果要求,只能throw一个或多个类型,该如何增加这个约束条件:

void Function's name () throw(type of data 1,...,type of data n)
{
    throw X;//must form data 1 to data n
}

如下:只能throw出去int类型的数据。 

void function_Error() throw(int)
{
    throw 1;
}

当然了,不写throw()可以抛出任意的数据类型。 

只写throw(),不能抛出异常。

7异常变量的生命周期

自定义类:

class MyException
{
public:
    MyException()
    {
        qDebug()<<"默认构造函数";
    }
    ~MyException()
    {
        qDebug()<<"析构函数";

    }
    MyException(const MyException &  c)
    {
        qDebug()<<"拷贝构造函数";

    }

};

 具体操作:

void doWork()
{
    throw MyException();
}
void test()
{
    try
    {
        doWork();
    }
    catch(MyException e)
    {
        qDebug()<<"异常捕获";
    }
}

说明:

throw MyException();

匿名对象,调用构造函数。 

    try
    {
        doWork();
    }
    catch(MyException e)
    {
        qDebug()<<"异常捕获";
    }

catch 的 MyException e,相当于函数参数, MyException e = 匿名对象。这种情况自然是会调用拷贝构造函数的,多了一份开销,此时只要使用引用即可。

void test()
{
    try
    {
        doWork();
    }
    catch(MyException & e)
    {
        qDebug()<<"异常捕获";
    }
}

 我们再看下面这种情况:

把引用换成指针:

void doWork()
{
    MyException e;
    throw &e;
}
void test()
{
    try
    {
        doWork();
    }
    catch(MyException * e)
    {
        qDebug()<<"异常捕获";
    }
}

 

在doWork完成后,直接销毁了e,那么在catch中就没有办法再使用任何MyException的接口,因为已经被销毁了。 

放在堆区: 

void doWork()
{
    MyException * e  = new MyException;
    throw e;
}

需要在catch中进行delete

void test()
{
    try
    {
        doWork();
    }
    catch(MyException * e)
    {
        qDebug()<<"异常捕获";
        delete e;
    }
}

 

 综上:使用引用最好

8C++标准的异常

和自定义异常的捕获很像,只是这个时候,程序内部写好了throw,只要你写好try和catch就行 

(图源:菜鸟教程) 

举例:最常见的指针越界问题

包含头文件:

#include<stdexcept>//trycatch

 执行代码:

     string str = "I see a monsters";
     string::iterator S;
     for(S = str.begin();S!=str.end();++S)
     {
         try
         {
             cout<<*S<<endl;
         }
         catch(out_of_range & e)
         {
             cout<<endl<<e.what()<<endl;
         }
     }

和自定义异常一样

    try
    {
        //保护程序
    }
    catch(系统异常类名 Name_Object)
    {
        Name_Object.what();//调用what接口

    }

在这里,what() 是异常类提供的一个公共方法。

9多态

看代码:父类引用指向子类对象

class BaseException
{
public:
    virtual void printError()
    {
        qDebug()<<"空指针"<<endl;
    }
};
class OutOfRangeExcept:public BaseException
{
public:
    virtual void printError()
    {
        qDebug()<<"越界"<<endl;
    }

};
void deWork()
{
    //throw
    throw OutOfRangeExcept();
}
void test01()
{
    try
    {
        deWork();
    }
    catch(BaseException & e)
    {
        e.printError();
    }
}

输出:

显然,进入catch的时候,BaseException的引用指向了OutOfExcept的匿名对象,父类指针(引用)指向子类对象,形成(动态)多态。

发布了85 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41605114/article/details/104960530