C++之noexcept

目录

1.概述

2.noexcept作为说明符

3.noexcept作为运算符

4.传统throw与noexcept比较

5.原理剖析

6.总结


1.概述

        在C++中,noexcept是一个关键字,用于指定函数不会抛出异常。如果函数保证不会抛出异常,编译器可以进行更多优化,比如防止异常传播时的栈展开,或者生成更有效的代码。

        C++11之前我们可以使用throw抛出异常,但是随着C++11中移动语义的产生,throw不能很好的解决移动语义过程中的异常处理,不同于拷贝,移动的过程如果出错的话不能保证原来的数据是否受到影响,为此C++11引入了noexcept关键字来处理这个问题。

        C++11后,逐渐形成“函数要么可能发射异常,要么保证不会发生异常”的共识。并提出了关键字noexcept用于指明函数保证自己不会发生异常。

        示例如下:

constexpr int myAdd(int a, int b) noexcept {return a + b;
}

        把noexcept关键字放在函数声明和定义的括号后,表示该函数不会抛出任何异常。若函数抛出异常,则程序会终止。

        如果想要允许该程序抛出异常,则可在noexcept后添加false:

constexpr int myAdd(int a, int b) noexcept (false) {return a + b;
}

        此时允许函数抛出异常。

2.noexcept作为说明符

        函数使用noexcept声明时,会告诉编译器这个函数不会发生异常,进而让编译器使用效率更高的检测代码,如果真的发生异常时程序会调用terminate函数直接结束程序。
        noexcept还可以接收一个bool类型的参数,该参数必须是一个常量表达式用以决定函数是否可以抛出异常,默认是true。如:

void myFunction() noexcept {// 这个函数不会抛出异常
}//带参数
template <class T>
void myFunction() noexcept(std::is_class<T>::value){}

   noexcept也可以用于重载解析:

void myFunction() {// 这个函数可能会抛出异常
}void myFunction() noexcept {// 这个函数不会抛出异常
}

        noexcept既可以表征普通函数不发射异常,也可以用于表征成员函数不发射异常。

//普通函数
int add(int a, int b)noexcept
{return a+b;
}//成员函数
class People {
public:
People(std::string name, int age) :m_name{ name }, m_age{ age } {}
~People()noexcept = default;inline const int GetAge()const noexcept
{return m_age;
}inline const std::string GetName()const noexcept
{return m_name;
}private:std::string m_name{ "" };int m_age{ 0 };
};

3.noexcept作为运算符

作为运算符接收表达式参数返回bool类型,判断表达式是否可以抛出异常。

void no_exception()noexcept
{throw true;
}void exception()
{throw true;
}void myfunc()
{int a=10; int b=10;exception();int c= a+b;
}int test_noexcept_oper() {std::cout<<std::boolalpha<<noexcept(no_exception())<<"\n"<<noexcept(exception())<<"\n"<<noexcept(myfunc())<<"\n";return 0;
}

移动构造函数和移动赋值运算符通常被设计为不抛出异常,因为它们只是“窃取”资源而不是复制它们。在这些函数上使用noexcept可以确保它们不会抛出异常,并允许编译器进行额外的优化。

class MyClass {  
public:  // 移动构造函数  MyClass(MyClass&& other) noexcept : /* 初始化列表 */ {  // 实现资源的移动  }  // 移动赋值运算符  MyClass& operator=(MyClass&& other) noexcept {  // 实现资源的移动  return *this;  }  
};

 解决移动语义的风险

1)遇到风险直接编译报错

template<class T>
void swap(T& a,T& b)
noexcept(noexcept(T(std::move(a))))&&
noexcept(noexcept(T(std::move(b))))
{static_assert(noexcept(T(std::move(a)))&&noexcept(T(std::move(b))));T tmp(std::move(a));a=std::move(b);b=std::move(tmp);
}

2)遇到风险改用拷贝

template<class T>
void swap_impl(T& a,T& b,std::integral_constant<bool,true>)noexcept{T tmp(std::move(a));a=std::move(b);b=std::move(tmp);
}
template<class T>
void swap_impl(T& a,T& b,std::integral_constant<bool,false>){T tmp(a);a=b;b=tmp;
}
template<class T>
void swap(T& a,T& b)
noexcept(noexcept(swap_impl(a,bstd::integral_constant<bool,noexcept(T(std::move(a)))&&noexcept(a.operator=(std::move(b)))>())))
{swap_impl(a,b,std::integral_constant<bool,noexcept(T(std::move(a)))&&noexcept(a.operator=(std::move(b)))>());
}

4.传统throw与noexcept比较

        在C++中,传统的throw异常机制和noexcept说明符提供了两种处理运行时错误的方法,它们在用途和目的上有明显的不同。下面是关于这两者之间比较的一些关键点:

传统的throw异常机制

  1. 错误处理throw语句用于在代码中抛出一个异常,表示发生了一个运行时错误或异常情况。这允许程序员在代码的多个位置中定义可能的错误情况,并在一个集中的错误处理位置(通常是catch块)中处理它们。

  2. 传播性:当异常被抛出时,它会沿着调用栈向上传播,直到找到一个能够处理该异常的catch块。如果没有找到合适的catch块,程序将调用std::terminate()并终止。

  3. 灵活性:异常机制允许程序员在多个不同的函数或方法之间传播错误,而不必依赖返回值或错误代码来指示错误。这使得代码更加清晰和易于维护。

  4. 性能开销:异常处理机制在运行时有一定的性能开销,因为它涉及到堆栈展开、异常表查找和可能的函数调用(对于catch块)。因此,在性能敏感的代码区域中频繁使用异常可能会导致性能下降。

noexcept说明符

  1. 异常保证noexcept说明符用于指示一个函数是否可能抛出异常。当函数被标记为noexcept时,它承诺不会抛出任何异常(除非它是由另一个noexcept(false)的函数调用引起的)。

  2. 优化机会:当编译器知道一个函数是noexcept时,它可以执行一些优化,例如消除额外的异常处理代码,从而提高程序的执行速度。

  3. 提高异常安全性:如果一个noexcept函数确实抛出了异常,程序将调用std::terminate()并终止。这有助于确保在资源转移或关键操作期间不会因异常而导致资源泄漏或其他未定义行为。

  4. 设计约束noexcept提供了一种方式来限制函数的异常行为,这在设计高性能或低延迟的系统时尤为重要。通过使用noexcept,程序员可以确保某些关键操作不会因异常而中断。

比较

  • 用途throw用于在代码中抛出异常以指示错误情况,而noexcept用于指示函数是否可能抛出异常。
  • 性能throw/catch机制在运行时有一定的性能开销,而noexcept则可以提高性能,因为它允许编译器进行额外的优化。
  • 错误处理throw/catch提供了一种灵活的错误处理机制,允许程序员在多个不同的函数或方法之间传播错误。而noexcept则更多地关注于函数的异常行为约束和性能优化。
  • 设计考虑:在设计高性能或低延迟的系统时,noexcept可能是一个重要的考虑因素,因为它可以确保关键操作不会因异常而中断。而在设计更一般性的库或应用程序时,throw/catch可能更为适用。

5.原理剖析

        noexcept保证函数不会发射异常,那么noexcept是如何保证的呢?为了分析这个问题,不妨让noexcept函数抛出异常,同时让普通函数抛出异常作为对照组,对比分析两个函数的行为。验证代码及行为如下:

//当noexcept函数触发异常时,会直接在函数内抛出异常的位置中断,异常未扩散。
//已在 xxx.exe 中执行断点指令(__debugbreak()语句或类似调用)。
void no_exception()noexcept
{throw true;
}//当常规函数触发异常时会提示异常;
//0x00007FFA2D8F543C 处(位于 xxxx.exe 中)有未经处理的异常:
// Microsoft C++ 异常: bool,位于内存位置 0x0000005B28B3F444 处。
void exception()
{throw true;
}

  由如上行为可知,noexcept函数在触发异常时直接中断,异常自然无法向外发射(传递)。

  正是由于其不向外发射异常特性,为编译器提供了更大的舞台。

  • 更大的优化空间:因为noexcept标注的函数,其异常不会向外传递,自然也就不存在开解调用栈(开解调用栈是指在异常处理、函数返回或程序终止过程中,系统自动执行的调用栈回溯和资源清理行为),也就给编译器更大的优化空间。

  • 提升性能:vector的push_back函数在扩容时,如果移动构造函数是noexcept形式时(is_nothrow_move_constructible_v)将使用移动来转移原有数据,而非之前的拷贝完成再删除的方式。使用“能移动则移动,必须拷贝再拷贝”的策略来提升性能。

注意事项

  • 只有在时间维度上恒为不发射异常的函数才可标注为noexcept,否则不要做出该函数noexcept的假设。

  • 如果函数标注为noexcept,则该函数调用的所有函数应也是noexcept,否则不要做出该函数noexcept的假设。尽管noexcept调用非noexcept函数会通过编译但不推荐这样做。

  • 不要为了使函数满足noexcept而修改函数,大可不必。

  • 释放内存的函数和析构函数默认为noexcept

6.总结

        综上所述,C++11通过引入noexcept关键字、智能指针、移动语义等特性,以及增强STL和其他库,显著提高了C++的异常安全性。这些特性使程序员能够更好地管理资源、避免资源泄漏和未定义行为,并在异常发时代码的健壮性和可靠性。

noexcept

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/847831.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言.数据结构.单链表经典算法

数据结构.单链表经典算法 1.经典算法OJ题1&#xff1a;移除链表元素1.1题目描述&#xff1a;1.2题解&#xff1a;1.3图文解释&#xff1a; 2.经典算法OJ题2&#xff1a;反转链表2.1题目描述&#xff1a;2.2题解&#xff1a;2.3图文解释 3.经典算法OJ题3&#xff1a;合并两个有序…

编译和运行qemu-uboot-arm64单板的Armbian系统

这篇文章ARM虚拟机安装OMV-CSDN博客遗留一个启动qemu-uboot-arm64单板Armbian镜像的问题&#xff0c;使用官方下载的镜像&#xff0c;会报错&#xff1a; fatal: no kernel available .... Failed to load /vmlinuz ...... qemu-system-aarch64 -smp 8 -m 8G -machine virt …

WPF音乐播放器 零基础4个小时左右

前言&#xff1a;winfrom转wpf用久的熟手说得最多的是,转回去做winfrom难。。当时不明白。。做一个就知道了。 WPF音乐播放器 入口主程序 FontFamily"Microsoft YaHei" FontSize"12" FontWeight"ExtraLight" 居中显示WindowStartupLocation&quo…

数据结构:并查集

数据结构&#xff1a;并查集 题目描述参考代码 题目描述 输入样例 5 5 C 1 2 Q1 1 2 Q2 1 C 2 5 Q2 5输出样例 Yes 2 3参考代码 #include <iostream>using namespace std;const int N 100010;int n, m; int p[N], sz[N];int find(int x) // 返回x的祖宗节点 路径…

山东大学软件学院项目实训-创新实训-基于大模型的旅游平台(二十六)- 微服务(6)

目录 10. Docker 10.1 Docker基本操作 10.1.1 镜像相关命令 10.1.2 容器相关命令 10.2 数据卷命令 10.2.1 常用命令 : 10.2.2 挂载数据卷 10. Docker 10.1 Docker基本操作 10.1.1 镜像相关命令 docker --help 查看docker帮助文档 docker images --help 查看docker ima…

Java中条件运算符的嵌套使用技巧总结

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

c语言项目-贪吃蛇项目2-游戏的设计与分析

文章目录 前言游戏的设计与分析地图&#xff1a;这里简述一下c语言的国际化特性相关的知识<locale.h> 本地化头文件类项setlocale函数 上面我们讲到需要打印★&#xff0c;●&#xff0c;□三个宽字符找到这三个字符打印的方式有两种&#xff1a; 控制台屏幕的长宽特性&a…

C++笔试强训day38

目录 1.天使果冻 2.dd爱旋转 3.小红取数 1.天使果冻 链接https://ac.nowcoder.com/acm/problem/219641 一开始都可以想到将数组的前x个数拿出来排降序输出第二个数即可。 但是因为询问量和数据量都较大&#xff0c;每次询问一次都要排序一次数组&#xff0c;会超时&#xf…

ai聊天机器人app的分享!有4个热门的软件!

在科技日新月异的今天&#xff0c;AI聊天机器人已经不再是遥不可及的科幻概念&#xff0c;而是实实在在走进了我们的日常生活。无论是工作中的信息查询&#xff0c;还是生活中的闲聊解闷&#xff0c;这些智能助手都能为我们提供便捷、高效的服务。那么&#xff0c;市面上都有哪…

含有嘧啶的光活性阳离子共轭微孔聚合物通过“吸附杀死”抗菌策略促进感染伤口愈合

引用信息&#xff1a; 文 章&#xff1a;Photoactive cationic conjugated microporous polymers containing pyrimidine with an adsorption-killantibacterial strategy for infected wound healing. 期 刊&#xff1a;Chemical Engineering Journal&#xff08;影响因子…

【MySQL】sql语句之库操作

序言 在上篇文章学习当中&#xff0c;我们认识了数据库的相关概念&#xff0c;以及MySQL的框架和基本使用等内容&#xff0c;总之对数据库有了一个大致的认识&#xff0c;那么本篇文章将开始关于sql语句的学习&#xff0c;本文主要是关于库的属性和操作的内容&#xff0c;简单可…

stm32下载驱动ST-LINK/V2驱动下载

http://www.openedv.com/docs/tool/dap/ST-LINKV2.html https://www.stmcu.com.cn/Designresource/detail/fi rmware_software/709492

电商风控指南:“仅退款”成部分商家梦魇,如何有效防控非法牟利

目录 “仅退款”成不法分子牟利新途径 各电商平台的“仅退款”条款模糊 商家如何防范“仅退款”的欺诈 “仅退款”服务已成为各大电商平台的“标配”。然而&#xff0c;以“薅羊毛”的方式谋取不当利益&#xff0c;给商家造成了经济和声誉上的双重损失&#xff0c;引发了广泛关…

CentOS-内网搭建FTP-Server

一、镜像选择 1、 Centos-everting或者DVD 2、7.5 7.6 7.9 均可 二、安装步骤 1、其余步骤和普通安装一致。 2、最重要的一步为“软件选择” 1、勾选FTP、文件以及存储服务器、性能以及开发工具。 三、FTPServer搭建 1、关闭防火墙 systemctl stop firewalld or 通过21和20…

机器视觉——找到物块中心点

首先先介绍一下我用的是HALCON中的HDevelop软件。 大家下载好软件后可以测试一下&#xff1a; 在程序编辑器窗口中输入下面指令&#xff1a; read_image(Image,monkey) 那么如果出现这样的图片&#xff0c;说明是没有问题的 那么本次编程采用的是下面这张图片 我们要达到的…

【论文复现|智能算法改进】基于改进麻雀算法的无线传感器网络覆盖优化研究

目录 1.算法原理2.改进点3.结果展示4.参考文献5.代码获取 1.算法原理 【智能算法】麻雀搜索算法&#xff08;SSA&#xff09;原理及实现 WSN数学模型 2.改进点 基于Sobol序列和ICMIC混沌映射的种群初始化 ICMIC是一种无线映射折叠次数的映射模型: { z n 1 sin ⁡ ( α π…

客户案例|Zilliz Cloud 助力点石科技转型 AI 智能服务商

福建点石科技网络科技有限公司成立于2010年&#xff0c;是国家高新技术企业&#xff0c;阿里云、蚂蚁金服等大厂海内外生态合作伙伴ISV。在餐饮、零售、酒店、旅游、商圈的行业定制化服务化上有深厚积累&#xff0c;在境内外做了大量标杆性软件项目&#xff0c;如东南亚RWS圣淘…

Python | 试卷刷题and基础笔记

1.下列转义字符中&#xff0c; 属于“回车”含义的是 \n 换行 \r 回车 2.for循环遍历字典 在Python中&#xff0c;你可以使用for循环来遍历字典的键&#xff08;keys&#xff09;、值&#xff08;values&#xff09;或者键-值对&#xff08;items&#xff09;。下面是三种遍历…

Postman接口测试笔记(超详细)

基于工具Poatman的接口自动化基础应用以及接口关联 一、什么是接口&#xff1f; 硬件接口&#xff1a;USB接口&#xff0c;投影仪接口&#xff0c;鼠标键盘接口 软件接口&#xff1a;称为API&#xff0c;主要使用于数据交互 软件接口分类&#xff1a; 内部接口&#xff1a…

树莓派5烧系统和ssh远程实现

1、硬件说明 树莓派5 64G micro SD卡读卡器 2、烧录系统过程记录 之前写过一篇pi4B烧录Ubuntu22.04的博客&#xff0c;这篇就简单记录备份下 2.1 去ubuntu官网在树莓派上安装Ubuntu | Ubuntu下载Ubuntu 桌面 24.04 LTS 我之前已经下好了就有个(1) 2.2 用读卡器把SD卡插到…