C++|异常处理


异常处理是设计来处理同步错误的,如数组下标越界、运算溢出、除数为0、无效的函数参数、内存分配失败(内存不足)等。
异常处理并不处理异步事件,如磁盘IO的完成、网络消息的到达、鼠标或键盘的敲击等,这些时间与程序控制流并行且相互独立。

1 定义一个异常类

定义异常类DivideByZeroException,继承自标准库类runtime_error(定义在头文件< stdexcept >)的一个派生类,runtime_error又是标准库exception(定义在头文件< exception >中)类的派生类。
runtime_error是C++用于描述运行时错误的标准基类。
exception是C++标准库用于描述所有异常而建的标准基类。所有异常类都直接或间接都含有virtrual函数what的exception继承而来,what函数返回一个异常对象的错误信息。
自定义异常类不一定要从C++的标准异常类派生而来。

#include <stdexcept>

class DivideByZeroExcception:public std::runtime_error
{
    public:
        DivideByZeroExcception():std::runtime_error("attempted to divide by zero.")
        {
        }
};

DivideByZeroException构造函数将错误信息传递给基类runtime_error。

2 throw, try{}…catch{}

#ifndef DIVIDEBYZEROEXCEPTION_H
#define DIVIDEBYZEROEXCEPTION_H

#include <stdexcept>

class DivideByZeroException:public std::runtime_error
{
    public:
        DivideByZeroException();
};

#endif
#include "DivideByZeroException.h"

using namespace std;

DivideByZeroException::DivideByZeroException()
            : runtime_error("attempted to divide by zero"){}
#include <iostream>
#include "DivideByZeroException.h"
using namespace std;

double quotient(int numerator,int denominator)
{
    if(denominator==0)
        throw DivideByZeroException();                    //构造函数,创建了一个                                  // DivideByZeroException类型匿名对象,并抛出异常
    return static_cast<double>(numerator)/denominator;
}
int main()
{
    int num1;
    int num2;

    cout << "Enter two integers ( end of file to end ) :";
    while(cin>>num1>>num2)
    {
        try
        {
            double result = quotient(num1, num2);
            cout << "The quotient is : " << result << endl;
        }
        catch(DivideByZeroException &divideByZeroException)     //接受异常对象引用
        {
            cout << "Exception occurred: " << divideByZeroException.what() << endl;   //打印由基类runtime_error.what()返回的异常信息
        }
        cout << "\nEnter two integers ( end of file to end ) : ";
        cout << endl;
    }
    
    return 0;
}

try语句块后可接多个catch以处理不同类型的异常。

异常终止模式:

当try 语句块发生异常,就会立即终止,搜索第一个能够处理此异常的catch处理器,匹配规则为catch参数类型与该异常类型相同或该异常是catch参数类型的派生类对象。异常处理结束后,程序不会回到try中的异常抛出点,而是从最后一个catch处理器后的第一条语句开始。

重新抛出异常:

一个函数可能会用到资源(如文件),并且发生一个异常时可能会释放资源(如关闭文件)。异常处理器收到异常时可以释放资源,然后通过重新抛出异常语句
throw;
通知这个函数的调用者进一步处理。

#include <iostream>
#include <exception>
using namespace std;

void throwException()
{
    try
    {
        cout << " Function throwException throws an exception.\n";  //函数处理
        throw exception();                              //发生异常,抛出
    }
    catch (exception&)                                  // 用不到对象成员,匿名即可
    {
        cout << " Exception handled in function throwException."
             << "\n Function throwException rethrows exception.";   //处理异常
        throw;                                          //重新抛出异常,给主函数处理
    }
    cout << "This also should not print\n";
}
int main()
{
    try
    {
        cout << "\nmain invokes function throwException\n";
        throwException();                            //调用一个函数
        cout << "This should not print.\n";
    }
    catch(exception&)
    {
        cout << "\n\nException handled in main.\n";       // 主函数mian进一步处理异常
    }
    
    return 0;
}

3 堆栈展开

异常被抛出但没有在一个特定作用域内被捕获时,函数调用堆栈就会展开(在其中异常没有被捕获的函数将会结束,相应局部变量销毁,并返回最初调用该函数的语句。如果该语句被一个try语句块封装,那么就会试图捕获该异常,否则继续堆栈展开,直至程序结束。),并试图在下一个外部的 try…catch 语句块内捕获这个异常。

对C++11,如果一个函数不会抛出任何异常,也不调用任何抛出异常的函数,应对其显式声明(在该函数原型和定义中的参数列表右侧添加 noexcept 关键字,对const成员函数,应放在const 之后),可以指示调用该函数不需要放在 try 语句中。

4 构造函数、析构函数的异常处理

1 、异常处理开始执行时,必须确保堆栈展开已经完成,如果由于堆栈展开而调用的析构函数抛出一个异常,程序将结束,这可能导致各种安全攻击。
2、如果一个对象有成员对象,在它的构造函数抛出异常前已经完成构造的成员对象的析构函数将被调用。
3、具有静态存储期的对象的构造函数抛出的异常无法被捕获。
4、new对象的构造函数抛出异常时,该对象动态内存将被释放。
5、构造函数抛出异常前,应释放它动态分配的内存。
6、异常可能阻止释放资源(内存资源或文件资源)代码的执行,导致资源泄漏,妨碍其它程序捕获资源。解决方法是初始化一个局部变量来获取资源。异常发生时,调用这个对象的析构函数释放资源。

5 处理new失败

new失败时会抛出一个 bad_alloc类型的异常(定义在< new >中),bad_alloc::what()可以打印异常信息。
另一种处理 new 失败的方法是使用 set_new_handler函数,参数为一个函数指针,指针指向的函数在 new 失败时被调用。不论 new 失败发生在什么位置,,一旦 set_new_handler 注册了处理器,new 运算符就不会抛出 bad_alloc 异常,它将错误推给 new 处理器函数处理。
new处理器函数应完成以下任务:

  1. 通过释放其它动态分配的内存来增加可用内存(或告诉用户关掉其他应用程序),并返回运算符new再次尝试分配内存。
  2. 抛出一个 bad_alloc 类型的异常
  3. 调用 abort 或 exit 函数(在头文件< cstdlib >中)来结束程序。
#include <iostream>
#include <new>
#include<cstdlib>
using namespcae std;

void customNewHandler()
{
    cerr << "customNewHandler was called";
    abort();
}
int main()
{
    double *ptr[50];
    set_new_handler(customNewHandler);
    for (size_t i = 0; i < 50;i++)
    {
        ptr[i] = new double[50000000];    // 可能会抛出异常
        cout << "i = " << i << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值