C++ 之 exception

时间:2014-10-10 23:57:54   收藏:0   阅读:302

本文讲关于C++的异常的所有东西:

绝对不让异常逃离析构函数


[插入: 什么时候会调用terminate函数呢?]
[回答 : By default, the terminate handler calls abort. But this behavior can be redefined by calling set_terminate.

This function is automatically called when no catch handler can be found for a thrown exception, or for some other exceptional circumstance that makes impossible to continue the exception handling process.
//对于一个异常没有相应的catch语句块的话,就会自动调用terminate函数,或者是对于一些无法处理的异常的情况时。

This function is provided so that the terminate handler can be explicitly called by a program that needs to abnormally terminate, and works even if set_terminate has not been used to set a custom terminate handler (calling abort in this case).

terminate被调用的情况:

1 当发送一个异常,并且构造函数产生异常
2 当发送一个异常,或者析构函数产生异常
3 一个静态对象的构造或者析构发送一个异常
4 以atexit注册的函数发生异常的时候
5 自定义一个异常,但是实际上没有异常产生的时候
6 调用缺省的unexcepted()函数时候 (unexcepted函数是因为该函数抛出了没有预期的异常)


2 可以协助确保destructor完成它应该完成的所有的动作。

#include <bits/stdc++.h>
using namespace std;

class myexception{};

class Session
{
public:
    Session()
    {
        logCreation();
    }

    ~Session()
    {
        try{ //这里的try catch块是很重要的
            logDestruction();
        }catch(...){
            cout << "catch exception..." << endl;
        }
    }

private:
    static void logCreation(){cout << "enter..." << endl;}
    static void logDestruction() {cout << "out..." << endl;throw myexception();}
};

int main()
{
    Session s;
    return 0;
}

catch的子语句块 VS.函数的调用

3大类的不同: 

1 exception 对象总是被复制。如果以by value的方式,甚至复制两次。 

2 , 被抛出作为exception的对象,允许的转换操作比“被传递到函数中”的少。

3   catch子句以其出现的顺序进行匹配,也就是 “first fit”的策略, 不像函数调用的 “best fit”的策略

class Base{
public:
    Base(){cout << "ctor in Base..." << endl;}
    Base(const Base &){cout << "copy ctor in Base..." << endl;}
};

class Derived : public Base {
public:
    Derived(){cout  << "ctor in Derived..." << endl;}
    Derived(const Derived &b):Base(b){cout << "copy ctor in Derived..." << endl;}
};

void f()
{
    Derived d;  // print : ctor in Base  ctor in Derived
    Base &b = d;
    cout << "throw..." << endl;
    throw b;  //copy ctor in Base
}

void test()
{
    try{
        f();
        cout << "never be here..." << endl;
    }catch(Base) // 如果这儿是Base,那么print: copy ctor in Base,如果是Base reference,那么不输出任何东西
    {
        cout << "catch..." << endl;
    }
}

int main()
{
    test();
    return 0;
}

下述的throw 和throw w分别来模拟catch块中的继续传递当前exception的操作。

//利用throw语句继续传递当前的exception,如果直接使用throw,那么传递的是最初传递来的exception的实际类型,(test1中的throw)
//如果是使用throw + catch形参的方式,那么传递的是静态类型。

class Base{
public:
    Base(){cout << "ctor in Base..." << endl;}
    Base(const Base &){cout << "copy ctor in Base..." << endl;}
};

class Derived : public Base {
public:
    Derived(){cout  << "ctor in Derived..." << endl;}
    Derived(const Derived &b):Base(b) {cout << "copy ctor in Derived..." << endl;}
};

void f1()
{
    try{
        Derived d;  // ctor in Base , ctor in Derived
        Base &b = d;
        cout << "throw..." << endl;
        //此处throw的是Derived对象
        throw d;  //copy ctor in Base  , copy ctor in Derived,这里看的是静态类型的!!!
    }catch(Base &w) //Base&是可以捕捉到上述的Derived异常的,无论此处是Base还是Base&,都能捕获到Derived类型的异常。
      //    如果此处是Derived,那么上述的throw如果throw的是b,那么这里不能捕获到。与一般的函数调用的参数绑定方式是一样的。
    {
        cout << "throw current exception 1, using throw only..." << endl;
        throw ; //如果直接使用throw,即使上面的catch是将一个Derived对象绑定到一个Base对象,这里传递的依然是最初的Derived对象。
    }
}

void test1()
{
    try{
        f1();
        cout << "never be here..." << endl;
    }catch(Derived)   //copy ctor in Base, copy ctor in Derived
    //}catch(Base) // copy  ctor in Base .
    {
        cout << "catch..." << endl;
    }
}

void f2()
{
    try{
        Derived d;  // print : ctor in Base  ctor in Derived
        Base &b = d;
        cout << "throw in f2..." << endl;
        throw b;  //copy ctor in Base //这里的throw传递的则是静态类型。
    }catch(Base &w)
    {
        cout << "throw current exception 2 " << endl;
        throw w; //copy ctor in Base
    }
}

void test2()
{
    try{
        f2();
        cout << "never be here in test2..." << endl;
    }catch(Base &) // 如果这儿是Base,那么print: copy ctor in Base,如果是Base reference,那么不输出任何东西
    {
        cout << "catch in test2..." << endl;
    }
}
int main()
{
    cout << "1 ; "<< endl;
    test1();
    cout << "2 : " << endl;
    test2();
    return 0;
}
void f(const string &s)
{
    cout << s << endl;
    cout << "const-reference..." << endl;
    return;
}

void f2(string &s)
{
    cout << "non-const reference..." << endl;
    return;
}

int main(void)
{
    char buff[20] = "1234";
    //f和f2函数需要的是一个string的对象,但是传递的都是buff字符数组,那么这里就会有一个隐式类型转换,生成一个临时的string对象。
    f(buff);   //调用成功。因为是const,不需要改变什么。
    f2(buff); //调用失败,原因:f2需要的是string的non-const reference,也就意味着f2函数可能会修改字符串s,而此时传递给
    //f2的是一个string的临时对象,在函数退出时,该实参没有改变,所有的操作均施加于临时形参s上,显然这并不是程序员的意图,我们传递一个
    //non-const reference给f2,应该就是希望修改buff这个实参的。
    return 0;
}

Catch exception By reference

理由:
+ 如果是by pointer的话,那么需要的是一个超出该作用域都不会被销毁的对象,那么此时global和static是可以帮忙的,但是程序员们可能会忘记这些;如果就是在throw的时候才临时的构造一个exception对象,比如: throw new exception,那么catch子句是否要删除这个对象呢?诚然,如果是global或者static,那么显然不需要删除,但是如果是new的,显然就需要删除。
+ 如果是by value的话,就会出现对象切割的问题,当把一个Derived exception的对象给一个Base exception的时候,那么会出现对象切割的问题,这个时候如果你在catch块中调用了虚函数,那么该函数的版本就只是基类的版本。显然这不是我们要的。
使用Catch exception by reference,便可以避免这些问题。

评论(0
© 2014 mamicode.com 版权所有 京ICP备13008772号-2  联系我们:gaon5@hotmail.com
迷上了代码!