C++ 异常处理

一、前言

异常处理使得程序在出现错误时能够得到及时的处理,以免程序崩溃或者未退出而引起更严重的错误。退出是无法接受的,严重的错误又很难查,这会使查问题的人崩溃。

二、为什么要使用异常

  1. 使检测问题和解决问题分离
  2. 使用返回码也可以,但是这容易导致信息不一致(0返回成功还是1返回成功,有的时候是相反的)
  3. 使用返回码占用返回值,c++可以使用一个参数来作为输出值,但这会使程序难懂
  4. 返回值可以人为忽略(或者忘记检查),这存在潜在风险,使得程序莫名其妙的退出而不知原因。异常不可人为忽略,这使得程序健壮些
  5. 异常信息丰富
  6. 异常可以传递, 这使得在底部出现的错误可以在上面的某一处集中处理即可,而不用层层处理

三、注意事项

四、基本语法

1.关键字

抛异常: throw
捕捉 :try/catch

2.函数声明

异常是在函数中抛出的,这些函数在声明时可以对内部的异常处理进行描述,即明确写出异常列表, 这有助于调用者了解内部机制。

1. void TestFunction();     
   表示该函数内部有可能抛出异常

2. void TestFunction() throw(int, char);  
   表示该函数内部可能抛出异常,但是若抛异常,那只能是int, char类型的,如果抛出其他类型的,可以正常编译,但是运行到该处时会调用terminate() ----> abort()

3. void TestFunction()throw(); 
    表示函数内部用于不会抛出异常,若抛出,可以正常编译,但是运行到该处时会terminate() ----> abort()

4. void TestFunction()throw(...); 
   表示该函数内部有可能抛出任何类型的异常

3.捕捉及捕捉顺序

try{
    throw E();
}catch(H h){
    //什么时候能执行到此处?见下面4条
}catch(H2 h){
    //H无法处理时会执行到此
}catch(...){
    //所有类型的异常都会被此处捕捉,不会向上层传递了,一般用于清理工作
    throw;//可以抛给上层处理
}
1. H和E类型相同。 如都是int
2. H是E的基类 。 一般不会这样调用,会产生切片(slice)效果,即信息不全
3. H 是 E的父类指针
4. H 是 E的引用。常用,如std::exception &e

异常只要被捕捉后就不会再向下传递, 所以如果本层无法处理, 那么可以在catch中重新抛出, 由上层处理, 再次抛出时, 只需要throw;即可,无需重新指定对象。

4.简单练习

class myException{ };

float divide(int a, int b)throw(myException)
{
    if0 == b)
    {
        throw myException();
    }
    else
    {
        return a/b;
    }
}

try{
    float value = divide(10,0);
}catch(myException &e){
    std::cout << "myException occur : 0 should not be denominator/*分母*/"std::endl;
}

5.标准C++异常

std::exception是c++的标准异常类, 我们在写自己的异常类时,应该尽量继承之。

1. 内容丰富,避免重造车轮子
2. 有助于调用者理解

c++11中exception()类的定义如下(noexcept 就是 throw()):

class exception {
public:
  exception () noexcept;
  exception (const exception&) noexcept;
  exception& operator= (const exception&) noexcept;
  virtual ~exception();
  virtual const char* what() const noexcept;
}

标准异常阶层体系如下:

当继承标准异常类时,应该重载父类的what函数和虚析构函数
virtual const char* what() const noexcept;

6. 实现自己的异常类

class TestException : public std::exception
{
public:
    TestException(int code, const std::string &msg):m_code(code), m_msg(msg){}
    virtual ~TestException() throw(){}
    virtual const char* what() throw()
    {
        return m_msg.c_str();
    }
    virtual int code()
        return m_code;
    }
private:
    std::string m_msg;
    int m_code = -1;
}

五来自C++之父Bjarne Stroustrup的建议

节选自《The C++ Programming Language》 ——C++之父Bjarne Stroustrup 
     1. Don’t use exceptions where more local control structures will suffice;    当局部的控制能够处理时,不要使用异常; 
     2. Use the "resource allocation is initialization" technique to manage resources;   使用“资源分配即初始化”技术去管理资源; 
     3. Minimize the use of try-blocks. Use "resource acquisition is initialization" instead of explicit handler code;    尽量少用try-catch语句块,而是使用“资源分配即初始化”技术。 
     4. Throw an exception to indicate failure in a constructor;     如果构造函数内发生错误,通过抛出异常来指明。 
     5. Avoid throwing exceptions from destructors;     避免在析构函数中抛出异常。 
     6. Keep ordinary code and error-handling code separate;      保持普通程序代码和异常处理代码分开。 
     7. Beware of memory leaks caused by memory allocated by new not being released in case of an exception;  小心通过new分配的内存在发生异常时,可能造成内存泄露。 
     8. Assume that every exception that can be thrown by a function will be thrown;    如果一个函数可能抛出某种异常,那么我们调用它时,就要假定它一定会抛出该异常,即要进行处理。 
     9. Don't assume that every exception is derived from class exception;     要记住,不是所有的异常都继承自exception类。 
     10. A library shouldn't unilaterally terminate a program. Instead, throw an exception and let a caller decide;    编写的供别人调用的程序库,不应该结束程序,而应该通过抛出异常,让调用者决定如何处理(因为调用者必须要处理抛出的异常)。 
     11. Develop an error-handling strategy early in a design;    若开发一个项目,那么在设计阶段就要确定“错误处理的策略”。

兄弟刚刚写博客,望诸位道上兄弟支持鼓励,欢迎任何形式的反馈,欢迎留言探讨,技术至上!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值