zoukankan      html  css  js  c++  java
  • 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; 重新抛出异常。

  • 相关阅读:
    九月腾讯,创新工场,淘宝等公司最新面试三十题
    java静态变量和实例变量的区别
    海量数据处理:十道面试题与十个海量数据处理方法总结
    持有对象(看think in java)
    在myeclipse9.0中安装插件SVN(掌握通用安装插件的方法)
    java的垃圾回收机制(think in java学习总结):
    CSS控制文本自动换行
    jquery获得select option的值 和对select option的操作
    JS操作table!js table行数
    jquery ui datepicker 只能选今天以后的日期
  • 原文地址:https://www.cnblogs.com/tianyajuanke/p/3217139.html
Copyright © 2011-2022 走看看