[C++]异常处理,你不会不清楚吧

Posted @书生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[C++]异常处理,你不会不清楚吧相关的知识,希望对你有一定的参考价值。

异常概念:

异常是C++引入的一种新的处理错误方式,当函数发现一个自己无法处理的错误的时候,就会抛出异常,交给函数调用者进行处理

C语言中处理错误的方式:

1.使用断言assert() 进行判断
2.返回错误码

C++异常:

C++中引入了三个关键字throw ,catch, try 来支持异常
throw: 当出现错误,进行抛出的时候,是使用throw完成的
catch: 在可能出现错误的地方,对异常进行捕捉,是使用catch完成的
try: try块中存放着可能出现异常的代码
示例:

int Sub(int a,int b)
{
    if (b == 0)
    {
    //除数为0,这种情况是不允许出现的,如果出现这样情况,就抛出异常
        throw 0;
    }
    return a / b;
}

int main() {
    
    try {
    //Sub函数可能会抛出异常,需要放入try块中
        int ret = Sub(7, 0);
        cout << ret << endl;
    }
    catch  (int err)
    {
        cout << err << endl;
    }
    return 0;
}

异常的某些规则

1.异常是通过抛出对象引发的,捕获的异常有哪部分代码处理,取决于catch的数据类型

int Sub(int a,int b)
{
    if (b == 0)
    {
        throw 0;
    }
    return a / b;
}

int main() {
    
    try {
        int ret = Sub(7, 0);
        cout << ret << endl;
    }
    catch  (int err)
    {
        cout <<"int"<<err<< endl;
    }
    catch (char err)
    {
        cout << "char" <<err<< endl;
    }
    return 0;

}

在这里插入图片描述
抛出int类型的对象,就会进入 catch int 类型的代码块
2.异常和捕捉的数据类型匹配存在一个例外,可以抛出派生类,通过基类进行捕捉

class Exception{
public:
    Exception(string str)
        :_str(str)
    {}
    virtual void what() = 0 ;
protected:
        string _str;
};

class SubErr : public Exception
{
public:
     SubErr()
         :Exception("除数为0")
     {}
     void what()
     {
         cout << _str << endl;
     }

};
int Sub(int a,int b)
{
    if (b == 0)
    {
        throw SubErr();
    }
    return a / b;
}

int main() {
    
    try {
        int ret = Sub(7, 0);
        cout << ret << endl;
    }
    catch (Exception& err)
    {
        err.what();
    }
    return 0;

}

在这里插入图片描述
3.抛出异常对象时,会生成一个异常对象的拷贝;

class Exception{
public:
    Exception(string str)
        :_str(str)
    {}
    virtual void what() = 0 ;
    void Printf()
    {
        cout << this << endl;
    }
protected:
        string _str;
};

class SubErr : public Exception
{
public:
     SubErr()
         :Exception("除数为0")
     {}
     void what()
     {
         cout << _str << endl;
     }

};
int Sub(int a,int b)
{
    if (b == 0)
    {
        SubErr se;
        se.Printf();
        throw se;
    }
    return a / b;
}

int main() {
    
    try {
        int ret = Sub(7, 0);
        cout << ret << endl;
    }
    catch (Exception& err)
    {
        err.Printf();
        err.what();
    }
    return 0;

}

在这里插入图片描述
4.catch(…)可以捕捉任何类型的对象,但是存在缺陷,无法知道异常对象的信息;
主要用于防止异常未被捕捉,造成程序崩溃的情况
5.异常被捕捉的位置一定时里异常抛出最近的位置

void Fun3()
{
    try {
    int ret = Sub(7, 0);
    cout << ret << endl;
    }
    catch (Exception& err)
   {
        cout << "Fun3()" << endl;
     err.what();
   } 
}
void Fun2()
{
    try {
        Fun3();
    }
    catch (int err)
    {
        cout << "Fun2()" << " int " << err << endl;
    }
}
void Fun1()
{
    try {
        Fun2();
    }
    catch (Exception& err)
    {
        cout << "Fun1()" << endl;
        err.what();
    }

}

int main() {
    
    try {
        Fun3();
    }
    catch (...)
    {
        cout << "main" << endl;
    }
    return 0;

}

上面的结果时Fun3();如果将Fun3()中的捕捉类型改为char那么打印的结果是Fun1(),大家可以使用代码自己去尝试一下;

由此我们也可以了解到异常的匹配原则:
先检查throw是否是在try块中,如果是的话,查找匹配的catch块;在某个函数中没有找到匹配的catch块,则退出函数栈,进入该函数的调用者去查找匹配的catch块,如果main函数中都没有匹配的catch块,则程序强行终止

异常可以重新抛出

在某些情况下,单独的一个catch块可能不足以解决所有问题,我们需要异常的重新抛出

void Fun1(int* a,int* b)
{
    try {
    int ret = Sub(*a, *b);
    cout << ret << endl;
    }
    catch (Exception& err)
   {
       
       err.what();
       throw "除数为0";
   } 
}
int main() {
    
    int* a = new int(7);
    int* b = new int(0);
    try {
        Fun1(a,b);
    }
    catch (...)
    {
        delete a;
        delete b;
        cout << "main" << endl;
    }
    return 0;

}

如果在Fun1()中不重新抛出异常的话,就会造成资源泄露的问题。

异常规范

我们可以在可能出现错误的地方,抛出任何异常;但不好的是,调用者和函数编写者不是同一个人,这就造成了函数抛出异常,但是函数调用者不知道该捕捉什么类型,当然可以使用…进行捕捉,但是这样就无法处理异常;因此C++提出了异常规范;你可以在函数后面加上 throw(函数中可能抛出的异常类型) 告诉调用者该捕捉什么类型;
如果使用throw() 表示该函数不会抛出任何异常;
如果没有说明,则可以抛出任何类型的异常

如果编译器支持异常规范的时候,当函数内抛出的异常类型和生命的类型不相同时,就会编译失败

int Sub(int a,int b)  throw(SubErr)//相当于做出承诺,指挥抛出SubErr类型的异常
{
    if (b == 0)
    {
        SubErr se;
        throw se;
    }
    return a / b;
}

int main() {
    
    try {
        int ret = Sub(7, 0);
        cout << ret << endl;
    }
    catch (SubErr& err)
    {

        err.what();
       
    }
    return 0;

}

在C++11中有引用了一个新的关键字noexcept

异常安全:

1.在构造函数和析构函数当中不要抛出异常,如果抛出异常的话,可能会导致初始化或者资源没有被完全释放掉;
2.异常会导致资源泄漏的问题,我们需要使用对象管理资源来防止内存泄漏

C++库的异常体系

在这里插入图片描述
其实根据C++库的异常体系,我们也可以自己继承exception或者设计新的基类来实现自己的自定义异常体系

异常的优缺点:

优点:
1.相比于错误码可以更好的展示错误信息,甚至可以包含堆栈的调用信息,可以更好的定位BUG
2.返回错误码的最大弊端是 深层函数发生错误,需要进行层层返回,到最外层(main)才能拿到错误码
3.部分函数使用异常更加好进行处理,比如有些成员函数是不能通过返回值的方法来返回错误码的
缺点:
1.异常时会导致执行流进行跳转的,我们在调试程序和分析程序的时候,可能会非常困难。
2.异常其实是有一定的性能损耗的。(由于现在的硬件性能,当然也可以忽略不计)
3.如果使用者不遵循异常规范,对于调用者来说,是很不方便的。(不知道应该捕捉什么异常)
4.C++标准库库中的异常体系有所欠缺
5.C++时没有垃圾回收机制的,使用异常有可能会造成内存泄漏。

注:如果本篇博客有任何错误和建议,欢迎伙伴们留言,你快说句话啊!

以上是关于[C++]异常处理,你不会不清楚吧的主要内容,如果未能解决你的问题,请参考以下文章

你不会还搞不清楚Spring Data JPA的关联关系注解如何使用吧?

异常和TCP通讯

在混合 C 和 C++ 代码编程中捕获异常后对象不会被破坏

Web层框架对网站中所有异常的统一处理

Web层框架对网站中所有异常的统一处理

使用源代码行信息处理 C++ 异常