• 第18章 用于大型程序的工具


    18.1异常处理

    try {
        // actions that cause an exception to be thrown
    }
    catch (...) {
        // work to partially handle the exception
        throw;
    }

    在C++中,通过throwing来raised一个exception。当throw时,throw后边的语句不再执行,转移到catch中,这意味:

    1. 沿着调用链的函数可能会提早退出
    2. 一点开始执行异常处理代码,沿着调用链创建的对象将被销毁
    3. 当throw一个exception时,会沿着函数调用链展开,寻找匹配的catch,如果没有找到,就会terminate。
    4. 展开过程中,对象被自动销毁
    5. 析构函数因为自动执行,所以外部无法捕获异常,他的异常应该在析构函数内部捕获并处理,如果没有处理,程序会terminate
    6. 抛出的对象如果是局部对象,将会因为展开过程中的自动销毁而不存在,抛出指针后所指向的对象必须保证存在。

    18.1.2捕获异常

    Catch中的异常声明,可以当成函数列表,如果使用可以为空。

    Catch中的异常对象如果非引用,将会发生拷贝

    特例的异常处理应该放在与异常最靠近的地方,一般的异常会截获特例

    在catch异常中,一般不允许类型转换(允许非const到const、派生类到基类、数组|函数转换到指针)

    在异常处理中,如果不能完全处理异常,可以再次抛出。

    Catch可以处理所有异常,可以使用【…】为参数

    18.1.3函数try语句块与构造函数

    要想处理构造函数初始值抛出的异常,必须将构造函数写成function try blocks,因为在构造初始值时(如下初始化data的时候),并未进入函数,所以在函数中无法处理,只能使用函数try语句块。(这个异常不能处理il构造时的异常)

    template <typenameT>
    Blob<T>::Blob(std::initializer_list<T> il) try :
        data(std::make_shared<std::vector<T>>(il)) {
        /* empty body*/
    }
    catch (const std::bad_alloc &e) 
    {
        handle_out_of_memory(e);
    }

    18.1.4异常说明noexcept

    1. 如果一个函数后有noexcept,表示这个函数不会抛出异常(声明和定义中都必须出现)。亦可以在一个函数指针的声明核定一种指定noexcept。
    2. 如果函数声明为noexcept,但是确实发生了异常,就会terminal,而并不会栈展开。

    判断一个调用是否是异常你一使用一元运算符noexcept(),并返回一个bool值指示是否会抛出异常。如果noexcept(true)放在一个函数后,表示此函数不会抛出异常,如果noexcept(false)放在一个函数后,表示此函数可能抛出异常。

    //如果g()确定不会抛出异常,返回true,则f()确定不异常
    void f() noexcept(noexcept(g()));

    异常说明与指针、虚函数和拷贝控制

    如果函数指针有noexcept说明符,则不能将没有noexcept声明的函数绑定到上边

    如果函数指针没有noexcept说明,则可以将任何函数绑定到上边。

    如果虚函数承诺了noexcept,则继承后的派生类也必须声明为noexcept

    对于合成的拷贝控制成员,如果其能够确定不会抛出异常,则合成的也是noexcept

    18.1.5异常类层次

    exception

    bad_alloc

    none

    logic_error

    domain_error

    invalid_argument

    out_of_range

    length_error

    runtime_error

    overflow_error

    underflow_error

    range_error

    bad_cast

    none

    自定义异常类

    class out_of_stock : public std::runtime_error 
    {
    public:
        explicit out_of_stock(const std::string &s) :
            std::runtime_error(s) { }
    };

    18.2命名空间

    //定义命名空间
    namespace my_namespace
    {
        //将其他空间中的成员在这里声明
        using std::cout;
        //命名空间别名
        namespace lib = std;
        void Foo()
        {
            //endl没有声明,必须写完整
            cout << "my namespace" << lib::endl;
        }
        //嵌套的命名空间,并且是内联的
        //内联的命名空间中的成员使用时不必完整写全
        inline namespace my_namespace_extends
        {
        }
    }
    class ClassName
    {
    };
    //模板特例化必须在原命名空间中
    namespace std
    {
        template <> struct hash<ClassName>;
    }
    //未命名的命名空间
    //其中变量拥有静态声明周期,第一次使用前创建,程序结束销毁
    //可以在一个文件中不连续,不能跨文件
    namespace
    {
    
    }

    18.2.3类、命名空间和作用域

    函数调用时,实参的命名空间会自动引入。

    18.3多重继承与虚继承

    1. 在某个给定的派生列表中,同一个基类只能出现一次。
    2. 派生类构造函数可以初始化它的直接基类。
    3. 派生类继承基类的构造函数,但是如果有多个基类中构造函数参数相同,则必须自定义这个构造函数,否则会出错。
    4. 如果一个函数重载对每个基类作为参数,则函数在派生类的对象做参数时,类型转换会有二义性错误
    5. 虽然派生列表中,一个基类可以出现一次,但是也可以通过间接多次继承同一个类。默认情况下,多次继承的类将对应多个独立的部分,存在于继承链的不同位置。
    6. 如果希望这个继承多次的类只对应一个部分,则使用虚继承。
    7. 但是需要注意,派生类继承时,其基类所对应的同一个间接基类之间的关系是虚拟继承时,才会实现虚继承。
      class Raccoon : public virtual ZooAnimal { /* ... */ };
      class Bear : virtual public ZooAnimal { /* ... */ };
      class Panda :Raccoon, Bear {/* ... */};
    8. 派生类的Panda需要独自控制其虚拟基类部分,如果Panda没有控制,则使用其默认构造函数,并且虚拟基类会首先被构造。
      class Panda :Raccoon, Bear
      {
          Panda(std::string name, bool onExhibit)
              :ZooAnimal(name, onExhibit, "Panda"),
              Bear(name, onExhibit),
              Raccoon(name, onExhibit),
              Endangered(Endangered::critical),
              sleeping_flag(false) { }
      };
    9. 析构函数是构造函数调用顺序的反序。
  • 相关阅读:
    JQ之html,text,val
    JQuery之编写弹窗
    DOM操作HTML元素属性
    DOM操作表格
    无缝滚动效果
    Date日期基础
    CISSP备考总结
    CISA考试大纲即将更新
    备考CISSP
    cisa备考体会
  • 原文地址:https://www.cnblogs.com/qiusuo/p/5154122.html
Copyright © 2020-2023  润新知