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\n";
        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

posted on 2016-05-30 17:41  迪米特  阅读(422)  评论(0编辑  收藏  举报

导航