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异常机制
-
错误处理:
throw语句用于在代码中抛出一个异常,表示发生了一个运行时错误或异常情况。这允许程序员在代码的多个位置中定义可能的错误情况,并在一个集中的错误处理位置(通常是catch块)中处理它们。 -
传播性:当异常被抛出时,它会沿着调用栈向上传播,直到找到一个能够处理该异常的
catch块。如果没有找到合适的catch块,程序将调用std::terminate()并终止。 -
灵活性:异常机制允许程序员在多个不同的函数或方法之间传播错误,而不必依赖返回值或错误代码来指示错误。这使得代码更加清晰和易于维护。
-
性能开销:异常处理机制在运行时有一定的性能开销,因为它涉及到堆栈展开、异常表查找和可能的函数调用(对于
catch块)。因此,在性能敏感的代码区域中频繁使用异常可能会导致性能下降。
noexcept说明符
-
异常保证:
noexcept说明符用于指示一个函数是否可能抛出异常。当函数被标记为noexcept时,它承诺不会抛出任何异常(除非它是由另一个noexcept(false)的函数调用引起的)。 -
优化机会:当编译器知道一个函数是
noexcept时,它可以执行一些优化,例如消除额外的异常处理代码,从而提高程序的执行速度。 -
提高异常安全性:如果一个
noexcept函数确实抛出了异常,程序将调用std::terminate()并终止。这有助于确保在资源转移或关键操作期间不会因异常而导致资源泄漏或其他未定义行为。 -
设计约束:
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
相关文章:
C++之noexcept
目录 1.概述 2.noexcept作为说明符 3.noexcept作为运算符 4.传统throw与noexcept比较 5.原理剖析 6.总结 1.概述 在C中,noexcept是一个关键字,用于指定函数不会抛出异常。如果函数保证不会抛出异常,编译器可以进行更多优化,…...
Kafka之Broker原理
1. 日志数据的存储 1.1 Partition 1. 为了实现横向扩展,把不同的数据存放在不同的 Broker 上,同时降低单台服务器的访问压力,我们把一个Topic 中的数据分隔成多个 Partition 2. 每个 Partition 中的消息是有序的,顺序写入&#x…...
RabbitMQ docker安装及使用
1. docker安装RabbitMQ docker下载及配置环境 docker pull rabbitmq:management # 创建用于挂载的目录 mkdir -p /home/docker/rabbitmq/{data,conf,log} # 创建完成之后要对所创建文件授权权限,都设置成777 否则在启动容器的时候容易失败 chmod -R 777 /home/doc…...
篇3:Mapbox Style Specification
接《篇2:Mapbox Style Specification》,继续解读Mapbox Style Specification。 目录 Spec Reference Root 附录: MapBox Terrain-RGB...
C#WPF数字大屏项目实战11--质量控制
1、区域划分 2、区域布局 3、视图模型 4、控件绑定 5、运行效果 走过路过,不要错过,欢迎点赞,收藏,转载,复制,抄袭,留言,动动你的金手指,财务自由...
第九十七节 Java面向对象设计 - Java Object.Finalize方法
Java面向对象设计 - Java Object.Finalize方法 Java提供了一种在对象即将被销毁时执行资源释放的方法。 在Java中,我们创建对象,但是我们不能销毁对象。 JVM运行一个称为垃圾收集器的低优先级特殊任务来销毁不再引用的所有对象。 垃圾回收器给我们一个…...
【scikit-learn009】异常检测系列:单类支持向量机(OC-SVM)实战总结(看这篇就够了,已更新)
1.一直以来想写下机器学习训练AI算法的系列文章,作为较火的机器学习框架,也是日常项目开发中常用的一款工具,最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下scikit-learn框架OCSVM模型相关知识体系。 3.欢迎批评指正,欢迎互三,跪谢一键三连! 4.欢迎…...
网络管理与运维
文章目录 网络管理与运维概念:传统网络管理:基于SNMP集中管理:基于iMaster NCE的网络管理:传统网络管理方式: 基于SNMP集中管理:交互方式:MIB:版本:SNMPv3配置网管平台&a…...
数据库查询字段在哪个数据表中
问题的提出 当DBA运维多个数据库以及多个数据表的时候,联合查询是必不可少的。则数据表的字段名称是需要知道在哪些数据表中存在的。故如下指令,可能会帮助到你: 问题的处理 查找sysinfo这个字段名称都存在哪个数据库中的哪个数据表 SELEC…...
第 400 场 LeetCode 周赛题解
A 候诊室中的最少椅子数 计数:记录室内顾客数,每次顾客进入时,计数器1,顾客离开时,计数器-1 class Solution {public:int minimumChairs(string s) {int res 0;int cnt 0;for (auto c : s) {if (c E)res max(res, …...
数据结构与算法之Floyd弗洛伊德算法求最短路径
目录 前言 Floyd弗洛伊德算法 定义 步骤 一、初始化 二、添加中间点 三、迭代 四、得出结果 时间复杂度 代码实现 结束语 前言 今天是坚持写博客的第18天,希望可以继续坚持在写博客的路上走下去。我们今天来看看数据结构与算法当中的弗洛伊德算法。 Flo…...
Ubuntu系统设置Redis与MySQL登录密码
Ubuntu系统设置Redis与MySQL登录密码 在Ubuntu 20.04系统中配置Redis和MySQL的密码,您需要分别对两个服务进行配置。以下是详细步骤: 配置Redis密码 打开Redis配置文件: Redis的配置文件通常位于/etc/redis/redis.conf。 sudo nano /etc/redis/redis.c…...
数据库连接池的概念和原理
目录 一、什么是数据库连接池 二、数据库连接池的工作原理 1.初始化阶段: 2.获取连接: 3.使用连接: 4.管理和优化: 三、数据库连接池的好处 一、什么是数据库连接池 数据库连接池(Database Connection Pooling&…...
国内常用的编程博客网址:技术资源与学习平台
一、国内常用的编程博客网址:技术资源与学习平台 大家初入编程,肯定会遇到各种各样的问题。我们除了找 AI 工具以外,我们还能怎么迅速解决问题呢? 大家可以通过谷歌,百度,必应,github…...
怎么给三极管基极或者MOS管栅极接下拉电阻
文章是瑞生网转载,PDF格式文章下载: 怎么给三极管基极或者MOS管栅极接下拉电阻.pdf: https://url83.ctfile.com/f/45573183-1247189078-52e27b?p7526 (访问密码: 7526)...
Java Web学习笔记5——基础标签和样式
<!DOCTYPE html> html有很多版本,那我们应该告诉用户和浏览器我们现在使用的是HMTL哪个版本。 声明为HTML5文档。 字符集: UTF-8:现在最常用的字符编码方式。 GB2312:简体中文 BIG5:繁体中文、港澳台等方式…...
01_深度学习基础知识
1. 感知机 感知机通常情况下指单层的人工神经网络,其结构与 MP 模型类似(按照生物神经元的结构和工作原理造出来的一个抽象和简化了模型,也称为神经网络的一个处理单元) 假设由一个 n 维的单层感知机,则: x 1 x_1 x1 至 x n x_n xn 为 n 维输入向量的各个分量w 1 j…...
60、最大公约数
最大公约数 题目描述 给定n对正整数ai,bi,请你求出每对数的最大公约数。 输入格式 第一行包含整数n。 接下来n行,每行包含一个整数对ai,bi。 输出格式 输出共n行,每行输出一个整数对的最大公约数。 数据范围 1 ≤ n ≤ 1 0 5 , 1≤n≤…...
设计模式在芯片验证中的应用——迭代器
一、迭代器设计模式 迭代器设计模式(iterator)是一种行为设计模式, 让你能在不暴露集合底层表现形式 (列表、 栈和树等数据结构) 的情况下遍历集合中所有的元素。 在验证环境中的checker会收集各个monitor上送过来的transactions࿰…...
imx6ull - 制作烧录SD卡
1、参考NXP官方的手册《i.MX_Linux_Users_Guide.pdf》的这一章节: 1、SD卡分区 提示:我们常用的SD卡一个扇区的大小是512字节。 先说一下i.MX6ULL使用SD卡启动时的分区情况,NXP官方给的镜像布局结构如下所示: 可以看到,…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)
目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
高防服务器价格高原因分析
高防服务器的价格较高,主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因: 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器,因此…...
