• C++的异常处理


    异常处理在C++中的地位是很尴尬的,他不被很多公司或者程序员认可,但是基于某些原因,个人依然觉得异常处理在C++程序中 是非常必要的。

    一般来说,异常分为两大类,一个是抛出异常,另一个是接受异常然后处理。

    • 抛出异常使用throw。
    • 接受异常 使用try....catch 语句块。

    1.标准异常抛出

    一般来说,标准库中类或者函数会在使用错误的时候抛出一些异常,这其实也被人诟病的,毕竟错误的使用库是程序员的责任,标准库不应该为此承担。举一个string的栗子:

    try{
     std::string wrolen("234");
     std::string it = wrolen.substr(10);
    }catch(std::exception& e){
        std::cout << "Something Wrong:"<<e.what() <<"
    ";
    }
    
    

    这个例子将只有3个字符的wrolen,取其10个字符长度的子串,自然会发生错误。
    结果为
    what(): basic_string::substr: __pos (which is 10) > this->size() (which is 3)

    使用try--catch后,程序不会立刻奔溃,而是执行打印语句,这说明在try语句中有string的异常被抛出。而我们刚有接住了这个异常。

    我们来看完整的测试代码

    #include <iostream>
    #include <string>
    
    void doExceptionCatch()
    {
        try{
            std::string wrolen("234");
            std::string it = wrolen.substr(10);
        }catch(std::exception &e){
            std::cout << "Something Wrong:"<<e.what() <<"
    ";
        }
    }
    
    int main(int argc, char const *argv[])
    {
      
        try{
            doExceptionCatch();
        }
        catch(std::out_of_range &e){
            std::cout << "In main handle Something Wrong:"<<e.what() <<"
    ";
        }
        return 0;
    }
    

    测试结果
    Something Wrong:basic_string::substr: __pos (which is 10) > this->size() (which is 3)

    在doExceptionCatch函数中使用了catch后,本来的异常已经被处理了,因此,回到main函数后,没有catch到任何异常,故没有打印。

    一个特意的改动是,在doExceptionCatch函数的catch中加一个throw关键字,于是这个异常会被继续上抛,被main捕获。

    事实上,标准库中定义了很多种异常,实际编码中,你可以在你认为会产生异常的地方将它们抛出来。当然了,一定是确实产生了异常。

    看下面的例子:

    int returnInt(unsigned int val)
    {
        return val > 2147483647 ? throw std::out_of_range("too big for a interger!"):val;
    }
    
    int main(int argc, char const *argv[])
    {
        try{
            int intme = returnInt(2147483648);
            std::cout << "In main intme :"<<intme <<"
    ";
        }catch(std::out_of_range &e){
            std::cout << "In main handle Something Wrong:"<<e.what() <<"
    ";
        }
        return 0;
    }
    

    我们知道一个int的最大数值是2147483647,因此当用户输入的数比这个还大,那么我们则抛出一个超出范围的异常。这合情合理。当函数returnInt没有抛出异常时,程序将正常执行,打印输入的数字,否则不会执行打印,而是直接进入到catch语句块。实现了类似(事实上也是)goto语句的功能。

    2.自定义的异常抛出

    标准库当然不会具体到项目的方方面面,可能你还需要自行定义一些异常情况,比如网络中断的处理,文件无法打开的处理等,那么你就可以自己定义异常了。一般来说,我们会继承标准库的std::exception来自定义异常类。

    class MyException:public std::exception
    {
    public:
        //重载标准exception的what函数
        const char* what() const noexcept{
            return "Error for my Diy exception!";
        }
    
    };
    void throwMyexception() noexcept(false)//noexcept(false)表明该函数可以抛出异常,throw(MyException),在C++11中已经废弃了这种写法
    {
        //这里模拟发生的异常,然后抛出自定义异常
        if(!false){
            throw MyException();
        }
    }
    int main(int argc, char const *argv[])
    {
        try{
            throwMyexception();
        }catch(MyException & e){
            std::cout << "my own exception is : "<< e.what() <<"
    ";
        }
        return 0;
    }
    
    

    值得一提的是noexcept 关键字,该关键字在C++11中引入,用于标识是否会抛出异常。

    3.异常捕获

    上面的例子中,已经涉及到如何捕获异常了,这里做个说明,不管是标准异常还是自定义的 异常,只要是可能存在异常抛出的位置,我们都可以使用try---catch语句块取处理异常。

    4.异常处理的时机

    异常处理不是神丹妙药,它不能也不应该作为你的软件出错后的救命稻草(如果软件要奔溃,就该让它早点奔溃),它应该作为软件在不该发生某些问题下的补充处理,虽然与此同时,这样的补充会引发代码膨胀,甚至于滥用。

    在《程序员的修炼》一书中,作者对异常的使用时机是这样认为的,如果去掉异常处理的部分,程序和之前表现一致,则说明异常处理是正确的引入了,否则,如果一个原本异常的代码,在异常处理后变正常了,则说明异常处理做了不该做的事。

    故此,我的理解是,异常处理不应该试图修复异常本身,而是应该将问题尽早暴露然后即时的对此做出处理,这个处理不是修复,修复应该是正常代码该做的事。比如本文第一个例子中,代码错误的取用了超出字符串长度的子串,这里就不该使用异常处理(好吧,我给自己打脸了),更好的方案是先判断字符串的长度,然后再取子串。第二个例子,用户(调用者)错误的输入了大于最大int型的数值,那么可以使用异常,异常处理里绝不是要把这个输入值变得小一些,使之合法,而是忽视用户的调用输入,并给予提示。那么为什么不使用if语句直接判断输入值是否大于最大int呢?这就涉及到异常的代码段的集中处理的优势了,它能让代码看起来更紧凑,不然,如果这里的判断多了,if就该满天飞了。
    这篇文章对异常的好处做了很详细的诠释

    话分两头,不使用异常也是可以的,但是观察C++的语言发展,标准会更倾向于加入并优化异常的处理。除了极端的,对速度有超高要求的场景外,我们需要引入异常,以避免程序在毫无防备的地方奔溃退出,让所有的异常都得到处理,就可以将软件的健壮性提升,如果你的代码写得很烂,那就更该引入异常处理,这样的话,奔溃时,程序至少能“死有所归”。

  • 相关阅读:
    xml 总结(一)数据岛,命名空间
    activiti designer 安装到 myeclipse
    activiti5.15 学习笔记
    goole网址IP
    form 中Enctype=multipart/form-data 的作用
    上传文件form表单enctype="multipart/form-data"传值解决办法(代原代码)
    淘宝初始化样式
    js闭包使用之处
    CSS Sprites
    iframe用的场景
  • 原文地址:https://www.cnblogs.com/Stultz-Lee/p/10111211.html
Copyright © 2020-2023  润新知