C++11 新特性 —— 关键字noexcept

本文深入解析C++11中noexcept关键字的使用场景与优化作用,包括其在移动构造、移动赋值、析构函数及叶子函数中的应用,以及如何通过条件noexcept提升代码稳定性。

转载;https://www.cnblogs.com/sword03/p/10020344.html

1 关键字noexcept

从C++11开始,我们能看到很多代码当中都有关键字noexcept。比如下面就是std::initializer_list的默认构造函数,其中使用了noexcept。

      constexpr initializer_list() noexcept
      : _M_array(0), _M_len(0) { }

该关键字告诉编译器,函数中不会发生异常,这有利于编译器对程序做更多的优化。
如果在运行时,noexecpt函数向外抛出了异常(如果函数内部捕捉了异常并完成处理,这种情况不算抛出异常),程序会直接终止,调用std::terminate()函数,该函数内部会调用std::abort()终止程序。

2 C++的异常处理

C++中的异常处理是在运行时而不是编译时检测的。为了实现运行时检测,编译器创建额外的代码,然而这会妨碍程序优化。
在实践中,一般两种异常抛出方式是常用的:

  • 一个操作或者函数可能会抛出一个异常;
  • 一个操作或者函数不可能抛出任何异常。

后面这一种方式中在以往的C++版本中常用throw()表示,在C++ 11中已经被noexcept代替。

    void swap(Type& x, Type& y) throw()   //C++11之前
    {
        x.swap(y);
    }
    void swap(Type& x, Type& y) noexcept  //C++11
    {
        x.swap(y);
    }

3 有条件的noexcecpt

在第2节中单独使用noexcept,表示其所限定的swap函数绝对不发生异常。然而,使用方式可以更加灵活,表明在一定条件下不发生异常。

    void swap(Type& x, Type& y) noexcept(noexcept(x.swap(y)))    //C++11
    {
        x.swap(y);
    }

它表示,如果操作x.swap(y)不发生异常,那么函数swap(Type& x, Type& y)一定不发生异常。

一个更好的示例是std::pair中的移动分配函数(move assignment),它表明,如果类型T1和T2的移动分配(move assign)过程中不发生异常,那么该移动构造函数就不会发生异常。

    pair& operator=(pair&& __p)
    noexcept(__and_<is_nothrow_move_assignable<_T1>,
                    is_nothrow_move_assignable<_T2>>::value)
    {
        first = std::forward<first_type>(__p.first);
        second = std::forward<second_type>(__p.second);
        return *this;
    }

4 什么时候该使用noexcept?

使用noexcept表明函数或操作不会发生异常,会给编译器更大的优化空间。然而,并不是加上noexcept就能提高效率,步子迈大了也容易扯着蛋。
以下情形鼓励使用noexcept:

  • 移动构造函数(move constructor)
  • 移动分配函数(move assignment)
  • 析构函数(destructor)。这里提一句,在新版本的编译器中,析构函数是默认加上关键字noexcept的。下面代码可以检测编译器是否给析构函数加上关键字noexcept。
    struct X
    {
        ~X() { };
    };
    
    int main()
    {
        X x;
    
        // This will not fire even in GCC 4.7.2 if the destructor is
        // explicitly marked as noexcept(true)
        static_assert(noexcept(x.~X()), "Ouch!");
    }
  • 叶子函数(Leaf Function)。叶子函数是指在函数内部不分配栈空间,也不调用其它函数,也不存储非易失性寄存器,也不处理异常。

最后强调一句,在不是以上情况或者没把握的情况下,不要轻易使用noexception。

### C++11 新特性列表及详细说明 C++11 是对 C++98 和 C++03 的重要升级版本,引入了许多新的特性和改进,极大地增强了语言的功能性、安全性以及开发效率。以下是 C++11 中的主要新特性及其详细说明: #### 1. **自动类型推导 (auto)** `auto` 关键字允许编译器根据变量的初始化表达式自动推导其类型,从而减少显式的类型声明并提高代码可读性[^2]。 ```cpp auto i = 42; // 推导为 int auto d = 3.14; // 推导为 double auto s = "hello"; // 推导为 const char* ``` --- #### 2. **统一的列表初始化 ({})** C++11 扩展了 `{}` 初始化的方式,使其能够应用于几乎所有类型的对象初始化,包括内置类型和用户自定义类型[^3]。 ```cpp int x1{ 1 }; // 整数初始化 std::vector<int> v{ 1, 2, 3 }; // STL 容器初始化 Point p{ 1, 2 }; // 自定义结构体初始化 ``` 这种初始化方式通过 `std::initializer_list` 实现,它是一个模板类,专门用于处理初始化列表的数据[^4]。 --- #### 3. **右值引用与移动语义 (Rvalue Reference and Move Semantics)** 右值引用 (`&&`) 提供了一种机制来区分左值和右值,并实现高效的资源转移操作——即所谓的“移动语义”,减少了不必要的拷贝开销[^2]。 ```cpp class MyClass { public: MyClass(MyClass&& other) noexcept { /* 移动构造函数 */ } }; MyClass obj1; MyClass obj2(std::move(obj1)); // 使用 std::move 显式触发移动语义 ``` --- #### 4. **Lambda 表达式** Lambda 表达式是一种匿名函数的形式,简化了回调函数和其他场景下的代码编写。 ```cpp auto lambda = [](int a, int b) -> int { return a + b; }; cout << lambda(3, 4); // 输出 7 ``` --- #### 5. **智能指针 (Smart Pointers)** 为了替代原始指针管理内存,C++11 引入了三种主要的智能指针:`std::shared_ptr`, `std::unique_ptr`, 和 `std::weak_ptr`,它们提供了更安全的动态内存管理方案。 ```cpp #include <memory> std::shared_ptr<int> sp(new int(42)); std::unique_ptr<int> up(new int(10)); ``` --- #### 6. **多线程支持 (Thread Support Library)** C++11 增加了原生的多线程编程能力,提供诸如 `std::thread`, `std::mutex`, `std::condition_variable` 等组件。 ```cpp #include <thread> void threadFunc() {} std::thread t(threadFunc); t.join(); ``` --- #### 7. **constexpr: 编译期常量计算** `constexpr` 可以标记某些函数或变量,使它们能够在编译期间完成求值,进一步优化性能。 ```cpp constexpr int square(int x) { return x * x; } static_assert(square(2) == 4, ""); // 验证编译期正确性 ``` --- #### 8. **新增的关键字与功能扩展** - **nullptr**: 替代传统的 NULL 或 0 来表示空指针,增强类型安全性[^2]。 - **override / final**: 控制虚函数的行为,防止意外重写或禁用继承[^2]。 - **using 别名声明**: 更灵活地创建类型别名[^5]。 ```cpp // nullptr 示例 int* ptr = nullptr; // override/final 示例 struct Base { virtual void f() = 0; }; struct Derived : Base { void f() override {} }; // using 类型别名 using IntVector = std::vector<int>; IntVector vec; ``` --- #### 9. **范围 for 循环 (Range-based For Loop)** 一种简洁的方式来遍历容器中的元素。 ```cpp std::vector<int> v = {1, 2, 3}; for(auto& elem : v) { ++elem; } ``` --- #### 10. **默认删除成员函数 (Defaulted/Deleted Functions)** 开发者可以通过显式指定 `default` 或 `delete` 来控制特殊成员函数的生成行为[^2]。 ```cpp class NonCopyable { public: NonCopyable(const NonCopyable&) = delete; // 删除拷贝构造函数 NonCopyable& operator=(const NonCopyable&) = delete; // 删除赋值运算符 }; ``` --- #### 11. **委托构造函数与继承构造函数** C++11 允许在一个类内部的不同构造函数之间互相调用(委托),或者让派生类直接使用基类的构造函数(继承)[^5]。 ```cpp struct Base { Base(int x) {} }; struct Derived : public Base { using Base::Base; // 继承基类构造函数 }; ``` --- #### 12. **其他改进** - **正则表达式支持 (`<regex>`):** 添加了强大的字符串匹配工具[^2]。 - **原子操作 (`atomic`):** 支持无锁并发编程[^2]。 - **时间库 (`chrono`):** 提供高精度的时间测量接口[^2]。 --- ### 总结 以上列举的是 C++11 中一些最重要的新特性,每项都旨在解决特定领域的问题或改善现有设计模式。掌握这些特性对于现代 C++ 开发至关重要,尤其是在阅读像 WebRTC 这样的大型开源项目时更是不可或缺[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值