• C++异常详解


    1.对异常的几种处理方式
    1)调用abort()
    abort()函数的原型位于头文件cstlib中,其实现是向标准错误流发送abnormal program termination(程序异常终止),然后终止程序。
    示例如下:

    #inclide<iostream>
    #inclide<cstlib>
    double hmean(double x,double y)
    {
        if(x==-y)
        {
            std::cout<<"arguments are not valid
    ";
            std::abort();
        }
        return 2*x*y/(x+y);
    }
    int main()
    {
        double x,y,z;
        while(std::cin>>x>>y)
        {
            z=hmean(x,y);
            std::cout<<"result is "<<z<<std::endl;
            std::cout<<"next loop "<<std::endl;
        }
        return 0;
    }

    2)返回错误码
    使用一个bool值来标记,运行结果是成功,还是失败。
    示例如下:

    #inclide<iostream>
    bool hmean(double x,double y,double &z)
    {
        if(x==-y)
        {
            return false;
        }
        z= 2*x*y/(x+y);
        return true;
    }
    int main()
    {
        double x,y,z;
        while(std::cin>>x>>y)
        {
            if(hmean(x,y,z))
            {
                std::cout<<"result is false!"<<std::endl;
            }
            else{
                std::cout<<"result is true!"<<std::endl;
                std::cout<<"result is "<<z<<std::endl;
            }
            std::cout<<"next loop "<<std::endl;
        }
        return 0;
    }

    3)使用全局变量errno
    出现异常时可以将全局变量errno设值,需要注意的是,要确保没有其他的函数同时在使用这个全局变量

    2.异常机制
    涉及try,catch,throw关键字
    示例代码如下:

    #inclide<iostream>
    double hmean(double x,double y)
    {
        if(x==-y)
        {
            throw "arguments are not valid";
        }
        return 2*x*y/(x+y);
    }
    int main()
    {
        double x,y,z;
        while(std::cin>>x>>y)
        {
            try
            {
                z = hmean(x,y);
            }
            catch(const char* s)
            {
                std::cout<<s<<std::endl;
                std::cout<<"next loop "<<std::endl;
                continue;
            }
            std::cout<<"result is "<<z<<std::endl;
            std::cout<<"next loop "<<std::endl;
        }
        return 0;
    }

    3.上面的示例中,我们抛出的是字符串,通常情况,我们会为每个可能出现的异常,定义一个异常类,当出现异常时,抛出该异常对象,catch块对该异常对象进行捕获。
    示例代码如下:

    #inclide<iostream>
    class bad_hmean
    {
    private:
        double x;
        double y;
    public:
        bad_hmean(int a=0,int b=0):x(a),y(b){}
        void mesg();
    };
    inline void bad_hmean::mesg()
    {
        std::cout<<"arguments are not valid "<<x<<" "<<y<<std::endl;
    }
    double hmean(double x,double y)
    {
        if(x==-y)
        {
            throw bad_mean(x,y);
        }
        return 2*x*y/(x+y);
    }
    int main()
    {
        double x,y,z;
        while(std::cin>>x>>y)
        {
            try
            {
                z = hmean(x,y);
            }
            catch(bad_hmean &hg)
            {
                hg.mesg();
                std::cout<<"next loop "<<std::endl;
                continue;
            }
            std::cout<<"result is "<<z<<std::endl;
            std::cout<<"next loop "<<std::endl;
        }
        return 0;
    }

    4.异常规范
    我们看下面的两行代码
    double hmean(int x,int y) throw(bad_thing)//可能抛出bad_thing异常
    double hmean(int x,int y) throw()//不抛出异常
    其中后面的throw()部分就是异常规范,指出该函数可能抛出的异常。
    程序员来确定可能抛出的异常,这样并不好。
    在C++11中,已经摒弃了该规范。

    5.栈解退

    栈解退是很重要的概念。为什么这么说呢,当抛出异常后,程序终止,或被catch块捕捉,我们必须要考虑内存的释放问题。

    我们先看一下,正常函数是如何处理内存释放的。
    函数调用时,调用函数的指令的地址会放到栈中,函数的参数或局部变量也将被添加到栈中或堆中。
    如果在其中又调用了函数,则执行同样的操作。
    当函数结束以后,程序会跳到被调用时存储的地址处,栈顶的元素被释放,同时释放其自动变量。
    如果自动变量时类对象,则类的析构函数将被调用。

    当函数出现异常时,程序也将不断释放栈,直到找到一个与该异常相对应的try块的返回地址
    随后,控制权将转到块尾的catch处理程序,而不是函数调用后面的第一条语句,这个过程称为栈解退。
    和正常函数调用不同的是,函数返回将处理该函数放在栈中的对象,而函数异常则处理,try块和throw之间放在栈中的对象。
    有了栈解退机制,引发异常后,也会释放调用中间函数时栈中的对象。

    我们看看下面的两个例子:

    void test1()
    {
        string mesg("hello");
        if(false)
            throw exception();
        return;
    }
    
    void test2()
    {
        double *ar = new double[n];
        if(false)
            throw exception();
        delete [] ar;
        return;
    }

    对于test1,函数异常后,会进行栈解退,string类析构函数会被调用,占用的内存将释放。
    对于test2,栈解退时,将删除栈中变量ar,但ar指向的内存块未释放,并且不可访问,会造成内存泄漏的问题。此时要如何处理呢?
    可以在引发异常的代码块中,捕获该异常,释放该内存块,然后重新引发异常。此时,内存块就被释放了。

    void test2()
    {
        double *ar = new double[n];
        try
        {
            if(false)
                throw exception();
        }
        catch(exception &ex)
        {
            delete[] ar;
            throw;
        }
        delete [] ar;
        return;
    }

    6.关于catch块

    try
    {}
    catch(exception &ex)
    {}

    注意catch块中参数为引用类型。当throw异常向上抛出时,该异常对象会被释放,此时catch块接收的异常对象为原始异常对象的一个副本。
    使用引用的目的是,基类引用可以执行派生类对象。若不使用引用,则只能调用基类的特性。

    7.C++标准异常库
    标准异常类exception在头文件exception中定义,类含有一个名为what()的虚拟成员函数。从exception派生的类可以重新定义它。
    C++库定义了很多基于exception的异常类型,此处不再详细介绍。

    参考资料:《C++ Primer.Plus》 pp.616-642

  • 相关阅读:
    Python 队列
    Python 栈
    Python面试百题
    TCP:四次挥手
    TCP:三次握手
    SQL:八 SQL高级处理
    SQL:七 集合运算
    SQL:六 函数、谓词、CASE表达式
    SQL:五 复杂查询
    python爬虫——爬取网页数据和解析数据
  • 原文地址:https://www.cnblogs.com/shijingjing07/p/5543316.html
Copyright © 2020-2023  润新知