• c++异常总结


    堆栈辗转开解(stack-unwinding):如果一个函数中出现异常,在当前函数内即通过 try..catch 捕捉(且捕捉到)的话,可以继续往下执行;如果不捕捉(或未捕捉到)就会抛出(与通过 throw 显式抛出效果相同)到外层函数,则当前函数会终止运行,释放当前函数内的局部对象(局部对象的析构函数就自然被调用了),外层函数如果也没有捕捉到的话,会再次抛出到更外层的函数,该外层函数也会退出,释放其局部对象……如此一直循环下去,直到找到匹配的 catch 子句,如果找到 main 函数中仍找不到,则调用名为 terminate 的标准库函数,该函数在 exception 头文件中定义,导致程序非正常退出。可以通过 std::set_terminate(fun) 来指定 terminate 调用的函数来输出一些信息。

    异常是通过抛出对象而引发的,抛出的对象类型决定应该激活哪个处理代码,被选中的处理代码是距离抛出异常最近的匹配的 catch 子句。

    #include <iostream>
    #include <string>
    
    void fun1() { throw "this is char* exception"; }
    
    void fun2() { throw std::string("this is string exception"); }
    
    int main()
    {
        try
        {
            fun1();
        }
        catch(const char* e)
        {
            std::cout<<e<<std::endl;
        }
        try
        {
            fun2();
        }
        catch(std::string& e)
        {
            std::cout<<e<<std::endl;
        }
    }

    标准库异常类:

    1、#include <exception>    

    定义了最常见的异常类,类名是 exception ,这个类只通知异常的产生,不提供更多信息。被 <stdexcept> 头文件包含

    2、#include <stdexcept>

    定义了几种常见的异常类,如: exception,runtime_error,range_error,out_of_range 等,都是 exception 的子孙类。

    3、#include <new>

    定义了 bad_alloc 异常类

    4、#include <type_info>

    定义了 bad_cast 异常类

    标准异常类型只定义了 what() 函数,返回 const char* 字符串,用来提供详细的异常描述信息。what() 返回什么,是由标准异常类的构造函数决定的,像 exception, bad_alloc, bad_cast 类型只有默认构造函数,所以错误信息是不可定制的,而其它标准异常类如 runtime_error 只定义了一个带 string 参数的构造函数,这种异常类型的 what() 就可以在构造函数时定制了。

    #include <iostream>#include <stdexcept>
    
    void fun() { throw std::runtime_error("this is my runtime_error"); }
    
    void fun2() { throw std::exception(); }
    
    int main()
    {
        try
        {
            fun();
            fun2();
        }catch(std::runtime_error& e)
        {
            std::cout<<e.what()<<std::endl;
        }
        catch(std::exception& e)
        {
            std::cout<<e.what()<<std::endl;
        }
    }

    可以自定义异常类,继承 exception ,实现 what() 函数: 

    #include <iostream>
    #include <string>
    #include <exception>
    
    class MyException : public std::exception
    {
        std::string error_msg;
    
    public:
        explicit MyException(const std::string& _error_msg):error_msg(_error_msg) { }
        const char* what()
        {
            return error_msg.c_str();
        }
        ~MyException() noexcept{}
        //旧式写法: ~MyException() throw(){}
        //throw(int) 只能抛出 int 型异常;throw() 不抛出任何异常
        //throw() 已经被 c++11 标记为过时,被 noexcept 取代
    };
    
    void fun() { throw MyException("this is myexception"); }
    
    int main()
    {
        try
        {
            fun();
        }
        catch(MyException& e)
        {
            std::cout<<e.what()<<std::endl;
        }
    }

    不继承 exception 类,也可以使用,那继承它有什么好处呢?

    1、统一的规范的接口,what() 函数。
    2、可以在捕捉语句中使用 catch(std::exception& e) 来捕捉。    

    如果被抛出的异常对象是派生类类型的,但由基类类型的 catch 处理,那么,catch 不能使用派生类的特有的任何成员,也不会有多态行为产生。

    另外,通过 catch(...) 可以捕捉所有的异常,包括自定义的异常(也包括不从 exception 继承的异常)。如果这样来看,其实不从 exception 继承也没什么,最后使用 catch(...) 把关。

    析构函数应该禁止抛出异常,因为当抛出异常时,局部对象析构,而如果此时该局部对象析构函数里也抛出异常的话,则 C++ 无法同时处理两个异常,就会调用 terminate 函数终止程序。如果析构函数里存在异常,应该在析构函数内部处理,而不应当抛出。

    构造函数中可以抛出异常,但发生异常时,该对象可能只是部分被构造,由于对象没有构造完成,不会执行析构函数,所以要自己保证能适当的撤销已构造的成员。C++拒绝为没有完成构造函数的对象调用析构函数,原因是避免开销,因为只有在每个对象里加一些字节来记录构造函数执行了多少步,它会使对象变大,且减慢析构函数的运行速度。所以只能自己手动进行资源释放,比较麻烦。一般建议不要在构造函数里做过多的资源分配,而应该把这些操作放在一个类似于 init 的成员函数中去完成。这样当 init 成员函数抛出异常时,如果对象是在栈上,析构函数仍会被调用(异常会自动销毁局部对象,调用局部对象的析构函数),如果是在堆上,需要在捕获异常之后 delete 对象来调用析构函数。

    在 catch 子句中,可以通过 throw; 重新抛出异常。

  • 相关阅读:
    在 Eclipse Workbench 之外使用 Eclipse GUI
    GB2312,GBK,Unicode
    木偶一之推荐系统
    Matlab:任意矩阵计算分布密度(海明距离的分布密度)
    live555在arm linux下的交叉编译,并下载的Arm板播放H264文件
    java设计模式之原型模式
    HDU 1102
    poj3661另一种做法(滚动数组)
    基于QT的小游戏细菌病毒战
    某代码查看器的保护突破
  • 原文地址:https://www.cnblogs.com/tianyajuanke/p/3217139.html
Copyright © 2020-2023  润新知