• c++ noexcept


    引入noexcept原因:

    1. 异常规范的检查是在运行期而不是编译期,因此程序员不能保证所有异常都得到了 catch 处理。
    2. 由于第一点的存在,编译器需要生成额外的代码,在一定程度上妨碍了优化。
    3. 模板函数中无法使用。赋值函数、拷贝构造函数和 do_something() 都有可能抛出异常,这取决于类型 T 的实现,所以无法给函数 func 指定异常类型。
    template<class T>
    void func(T k) {
        T x(k);
        x.do_something();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 实际使用中,我们只需要两种异常说明:抛异常和不抛异常,也就是 throw(…) 和 throw()。

    noexcept

    noexcept 紧跟在函数的参数列表后面,它只用来表明两种状态:“不抛异常” 和 “抛异常”

    void func_not_throw() noexcept; // 保证不抛出异常
    void func_not_throw() noexcept(true); // 保证不抛出异常
    
    void func_throw() noexcept(false); // 可能会抛出异常
    void func_throw(); // 可能会抛出异常,若不显示说明,默认是会抛出异常(除了析构函数,详见下面)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    对于一个函数而言,

    1. noexcept 说明符要么出现在该函数的所有声明语句和定义语句,要么一次也不出现。
    2. 函数指针及该指针所指的函数必须具有一致的异常说明。
    3. 在 typedef 或类型别名中则不能出现 noexcept。
    4. 在成员函数中,noexcept 说明符需要跟在 const 及引用限定符之后,而在 final、override 或虚函数的 =0 之前。
    5. 如果一个虚函数承诺了它不会抛出异常,则后续派生的虚函数也必须做出同样的承诺;与之相反,如果基类的虚函数允许抛出异常,则派生类的虚函数既可以抛出异常,也可以不允许抛出异常。

    需要注意的是,编译器不会检查带有 noexcept 说明符的函数是否有 throw。

    void func_not_throw() noexcept {
        throw 1; // 编译通过,不会报错(可能会有警告)
    }
    
    • 1
    • 2
    • 3

    程序会直接调用 std::terminate,并且不会栈展开(Stack Unwinding)(也可能会调用或部分调用,取决于编译器的实现)。另外,即使你有使用 try-catch,也无法捕获这个异常。

    #include 
    using namespace std;
    
    void func_not_throw() noexcept {
        throw 1;
    }
    
    int main() {
        try {
            func_not_throw(); // 直接 terminate,不会被 catch
        } catch (int) {
            cout << "catch int" << endl;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    noexcept 除了可以用作说明符(Specifier),也可以用作运算符(Operator)。noexcept 运算符是一个一元运算符,它的返回值是一个 bool 类型的右值常量表达式,用于表示给定的表达式是否会抛出异常。

    void f() noexcept {
    }
    
    void g() noexcept(noexcept(f)) { // g() 是否是 noexcept 取决于 f()
        f();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其中 noexcept(f) 返回 true,则上式就相当于 void g() noexcept(true)。
    析构函数默认都是 noexcept 的。C++ 11 标准规定,类的析构函数都是 noexcept 的,除非显示指定为 noexcept(false)。

    class A {
      public:
        A() {}
        ~A() {} // 默认不抛出异常
    };
    
    class B {
      public:
        B() {}
        ~B() noexcept(false) {} // 可能会抛出异常
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    在为某个异常进行栈展开的时候,会依次调用当前作用域下每个局部对象的析构函数,如果这个时候析构函数又抛出自己的未经处理的另一个异常,将会导致 std::terminate。所以析构函数应该从不抛出异常。

    特点

    • 显示指定 noexcept 的函数,编译器会进行优化

    调用 noexcept 函数时不需要记录 exception handler,所以编译器可以生成更高效的二进制码(编译器是否优化不一定,但理论上 noexcept 给了编译器更多优化的机会)。另外编译器在编译一个 noexcept(false) 的函数时可能会生成很多冗余的代码,这些代码虽然只在出错的时候执行,但还是会对 Instruction Cache 造成影响,进而影响程序整体的性能。

    • 容器操作针对 std::move 的优化

    一个 std::vector,若要进行 reserve 操作,一个可能的情况是,需要重新分配内存,并把之前原有的数据拷贝(copy)过去,但如果 T 的移动构造函数是 noexcept 的,则可以移动(move)过去,大大地提高了效率。

    #include 
    #include 
    
    using namespace std;
    
    class A {
      public:
        A(int value) {
        }
    
        A(const A &other) {
            std::cout << "copy constructor\n";
        }
    
        A(A &&other) noexcept {
            std::cout << "move constructor\n";
        }
    };
    
    int main() {
        std::vector<A> a;
        a.emplace_back(1);
        a.emplace_back(2);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这里插入图片描述
    把移动构造函数的 noexcept 说明符去掉,则会输出:
    在这里插入图片描述

    使用场景

    1. 析构函数

    这不用多说,必须也应该为 noexcept。

    1. 构造函数(普通、复制、移动),赋值运算符重载函数

    尽量让上面的函数都是 noexcept,这可能会给你的代码带来一定的运行期执行效率。

    1. 可以 100% 保证不会 throw 的函数

    比如像是 int,pointer 这类的 getter,setter 都可以用 noexcept。因为不可能出错。也可以看下准标准库 Boost 的源码,全局搜索BOOST_NOEXCEPT。

  • 相关阅读:
    五个步骤轻松搞定软件开发流程
    Spring boot原理
    改进YOLOv7系列:28.YOLOv7 结合 Swin Transformer V2结构,Swin Transformer V2:通向视觉大模型之路
    MySQL统计函数count详解
    小程序OCR身份证识别
    Git常用命令
    微服务理论知识
    提升网络安全防御能力的几个方面
    恶补《操作系统》2_1——王道学习笔记
    NIM游戏/SG函数
  • 原文地址:https://blog.csdn.net/weixin_44347020/article/details/132713554