C++的异常处理

异常处理在C++中的地位是很尴尬的,他不被不少公司或者程序员承认,可是基于某些缘由,我的依然以为异常处理在C++程序中 是很是必要的。ios

通常来讲,异常分为两大类,一个是抛出异常,另外一个是接受异常而后处理。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() <<"\n";
}

这个例子将只有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() <<"\n";
    }
}

int main(int argc, char const *argv[])
{
  
    try{
        doExceptionCatch();
    }
    catch(std::out_of_range &e){
        std::cout << "In main handle Something Wrong:"<<e.what() <<"\n";
    }
    return 0;
}

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

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

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

事实上,标准库中定义了不少种异常,实际编码中,你能够在你认为会产生异常的地方将它们抛出来。固然了,必定是确实产生了异常。code

看下面的例子:

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 <<"\n";
    }catch(std::out_of_range &e){
        std::cout << "In main handle Something Wrong:"<<e.what() <<"\n";
    }
    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() <<"\n";
    }
    return 0;
}

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

3.异常捕获

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

4.异常处理的时机

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

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

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

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

相关文章
相关标签/搜索