当前位置: 首页 > news >正文

C++ 语言特性20 - noexcept 关键字

一:概述

        noexcept 是 C++11 引入的一个关键字,用来指明一个函数在运行时是否不会抛出异常。它主要用于提高代码的安全性和性能,在某些场合可以帮助编译器进行优化。

1.  noexcept 的语法

//1. 标记函数为 noexcept, 在这种情况下,func 承诺不会抛出异常
void func() noexcept {// 函数体
}//2. 带条件的 noexcept
//noexcept 关键字也可以根据条件来动态判断一个函数是否是 noexcept。其条件必须是编译时常量表达式:
void func() noexcept(true);   // 一定不会抛出异常
void func() noexcept(false);  // 可能会抛出异常//在下面例子中,func 是否是 noexcept 取决于 some_other_func 是否是 noexcept 的。如果 some_other_func 是 noexcept,那么 func 也是 noexcept。
template <typename T>
void func(T&& t) noexcept(noexcept(some_other_func(std::forward<T>(t)))) {some_other_func(std::forward<T>(t));
}//3. noexcept 运算符
//C++ 提供了 noexcept 运算符,允许在编译时判断某个表达式是否为 noexcept。返回值是一个 bool 值,表示表达式是否不会抛出异常:
bool isNoexcept = noexcept(func());  // 如果 func 是 noexcept,返回 true

2. noexcept 的作用

  • 指定函数不抛出异常noexcept 表示一个函数承诺不会抛出任何异常。通过标记为 noexcept,编译器可以做更多的优化,例如跳过额外的异常处理机制。

    • 如果一个函数标记为 noexcept,但在运行时抛出了异常,程序会直接调用 std::terminate,结束程序的运行,而不会传播异常。

    • noexcept 可以用来提升代码的鲁棒性,使得调用方知道某些函数是不会抛出异常的。

  • 启用编译器优化: 在某些情况下,编译器可以为标记为 noexcept 的函数进行更多的优化。例如,当对象被复制或移动时,如果移动构造函数是 noexcept 的,标准容器如 std::vector 可以选择更高效的移动操作而不是复制操作。

二:使用场合

  • 移动构造函数和移动赋值运算符: 标记移动构造函数和移动赋值运算符为 noexcept 是非常重要的。当标准容器(如 std::vectorstd::deque)进行内存重新分配时,如果对象的移动操作是 noexcept 的,那么容器可以选择移动对象而不是复制对象,从而提升性能。
class MyClass {
public:MyClass(MyClass&&) noexcept;  // 移动构造函数MyClass& operator=(MyClass&&) noexcept;  // 移动赋值运算符
};//例如,当 std::vector 增长时,它需要移动现有元素到新的存储位置。如果移动构造函数是 noexcept 的,std::vector 会执行移动操作;否则,它会执行较慢的复制操作。
  • 不抛出异常的函数: 对于一些明确不会抛出异常的函数,使用 noexcept 可以帮助编译器生成更高效的代码。特别是一些低层次的库函数,比如内存管理相关的函数、数学运算函数、或对象析构函数等,通常标记为 noexcept 
void fastMathOperation() noexcept {// 这个函数不会抛出任何异常
}
  •  析构函数: 根据 C++ 标准,析构函数默认是 noexcept 的。即便不显式声明,析构函数通常不应该抛出异常。在某些场合下,特别是资源管理类(如 RAII 类),确保析构函数是 noexcept 的很重要。RAII(Resource Acquisition Is Initialization,资源获取即初始化)
class MyClass {
public:~MyClass() noexcept {  // 确保析构时不抛出异常// 清理代码}
};
  • 标准库中的使用: C++ 标准库广泛使用了 noexcept,尤其是在 std::move_if_noexcept 中。该函数用于决定是使用移动构造函数还是复制构造函数。当移动构造函数被标记为 noexcept 时,std::move_if_noexcept 会使用移动构造函数;否则,使用复制构造函数。
template <typename T>
typename std::conditional<!std::is_nothrow_move_constructible<T>::value && std::is_copy_constructible<T>::value,const T&, T&&>::type
move_if_noexcept(T& x) noexcept;
  •  模板元编程中的使用: 在模板元编程中,通过 noexcept 可以在不同的条件下生成不同的函数版本。例如,只有当某个操作不会抛出异常时,某个特定的版本才会被选择。

三:注意事项

  • noexcept 与异常的实际抛出: 如果标记为 noexcept 的函数抛出了异常,程序不会传播异常,而是直接调用 std::terminate 终止程序。因此,务必确保标记为 noexcept 的函数真的不会抛出异常。
     
  • 条件 noexcept 的使用: 使用条件 noexcept 时,确保条件表达式在编译时能够计算为常量。这样可以动态地决定函数是否应标记为 noexcept,从而在不同的场景下启用不同的异常处理机制。
     
  • 性能优化noexcept 关键字会帮助编译器进行优化,但这些优化在大多数情况下是微小的,尤其是对于非性能关键的代码,不需要过度使用 noexcept。但是,对于性能敏感的代码(如标准容器的实现、库函数等),合理使用 noexcept 可以带来显著的优化。
     
  • throw() 的区别: 在 C++11 之前,throw() 用于指定函数不抛出任何异常,但它已被 noexcept 取代。现在建议使用 noexcept 而不是 throw(),因为 throw() 已被弃用。


http://www.mrgr.cn/news/42713.html

相关文章:

  • python 实现djb2哈希算法
  • 8-2.Android 任务之 CountDownTimer 编码模板(开启计时器、取消计时器)
  • 【计算机网络】详解UDP协议格式特点缓冲区
  • 使用 Wireshark 抓取类似的 HTTP 请求包
  • 读书笔记_《组织行为学》
  • 8c语言基础文件
  • 补码加/减运算的具体示例
  • 《重生到现代之从零开始的C语言生活》—— 结构体和位段
  • python字典为什么至少需要哈希表的三分之一的行留空
  • linux常用的命令
  • 用Python和OpenCV实现人脸识别:构建智能识别系统
  • C++:const成员
  • 力扣 简单 100.相同的树
  • C语言第15课—数据在内存中的存储
  • 基于Zynq SDIO WiFi移植一(支持2.4/5G)
  • mysql设置表的某一个字段每天定时清零
  • 【数据结构】链表-1
  • C++基础(7)——STL简介及string类
  • js进阶——深入解析JavaScript中的URLSearchParams
  • 文心一言 VS 讯飞星火 VS chatgpt (361)-- 算法导论24.3 3题