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

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

相关文章:

C++之noexcept

目录 1.概述 2.noexcept作为说明符 3.noexcept作为运算符 4.传统throw与noexcept比较 5.原理剖析 6.总结 1.概述 在C中&#xff0c;noexcept是一个关键字&#xff0c;用于指定函数不会抛出异常。如果函数保证不会抛出异常&#xff0c;编译器可以进行更多优化&#xff0c;…...

Kafka之Broker原理

1. 日志数据的存储 1.1 Partition 1. 为了实现横向扩展&#xff0c;把不同的数据存放在不同的 Broker 上&#xff0c;同时降低单台服务器的访问压力&#xff0c;我们把一个Topic 中的数据分隔成多个 Partition 2. 每个 Partition 中的消息是有序的&#xff0c;顺序写入&#x…...

RabbitMQ docker安装及使用

1. docker安装RabbitMQ docker下载及配置环境 docker pull rabbitmq:management # 创建用于挂载的目录 mkdir -p /home/docker/rabbitmq/{data,conf,log} # 创建完成之后要对所创建文件授权权限&#xff0c;都设置成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、运行效果 走过路过&#xff0c;不要错过&#xff0c;欢迎点赞&#xff0c;收藏&#xff0c;转载&#xff0c;复制&#xff0c;抄袭&#xff0c;留言&#xff0c;动动你的金手指&#xff0c;财务自由...

第九十七节 Java面向对象设计 - Java Object.Finalize方法

Java面向对象设计 - Java Object.Finalize方法 Java提供了一种在对象即将被销毁时执行资源释放的方法。 在Java中&#xff0c;我们创建对象&#xff0c;但是我们不能销毁对象。 JVM运行一个称为垃圾收集器的低优先级特殊任务来销毁不再引用的所有对象。 垃圾回收器给我们一个…...

【scikit-learn009】异常检测系列:单类支持向量机(OC-SVM)实战总结(看这篇就够了,已更新)

1.一直以来想写下机器学习训练AI算法的系列文章,作为较火的机器学习框架,也是日常项目开发中常用的一款工具,最近刚好挤时间梳理、总结下这块儿的知识体系。 2.熟悉、梳理、总结下scikit-learn框架OCSVM模型相关知识体系。 3.欢迎批评指正,欢迎互三,跪谢一键三连! 4.欢迎…...

网络管理与运维

文章目录 网络管理与运维概念&#xff1a;传统网络管理&#xff1a;基于SNMP集中管理&#xff1a;基于iMaster NCE的网络管理&#xff1a;传统网络管理方式&#xff1a; 基于SNMP集中管理&#xff1a;交互方式&#xff1a;MIB&#xff1a;版本&#xff1a;SNMPv3配置网管平台&a…...

数据库查询字段在哪个数据表中

问题的提出 当DBA运维多个数据库以及多个数据表的时候&#xff0c;联合查询是必不可少的。则数据表的字段名称是需要知道在哪些数据表中存在的。故如下指令&#xff0c;可能会帮助到你&#xff1a; 问题的处理 查找sysinfo这个字段名称都存在哪个数据库中的哪个数据表 SELEC…...

第 400 场 LeetCode 周赛题解

A 候诊室中的最少椅子数 计数&#xff1a;记录室内顾客数&#xff0c;每次顾客进入时&#xff0c;计数器1&#xff0c;顾客离开时&#xff0c;计数器-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天&#xff0c;希望可以继续坚持在写博客的路上走下去。我们今天来看看数据结构与算法当中的弗洛伊德算法。 Flo…...

Ubuntu系统设置Redis与MySQL登录密码

Ubuntu系统设置Redis与MySQL登录密码 在Ubuntu 20.04系统中配置Redis和MySQL的密码&#xff0c;您需要分别对两个服务进行配置。以下是详细步骤&#xff1a; 配置Redis密码 打开Redis配置文件: Redis的配置文件通常位于/etc/redis/redis.conf。 sudo nano /etc/redis/redis.c…...

数据库连接池的概念和原理

目录 一、什么是数据库连接池 二、数据库连接池的工作原理 1.初始化阶段&#xff1a; 2.获取连接&#xff1a; 3.使用连接&#xff1a; 4.管理和优化&#xff1a; 三、数据库连接池的好处 一、什么是数据库连接池 数据库连接池&#xff08;Database Connection Pooling&…...

国内常用的编程博客网址:技术资源与学习平台

一、国内常用的编程博客网址&#xff1a;技术资源与学习平台 大家初入编程&#xff0c;肯定会遇到各种各样的问题。我们除了找 AI 工具以外&#xff0c;我们还能怎么迅速解决问题呢&#xff1f; 大家可以通过谷歌&#xff0c;百度&#xff0c;必应&#xff0c;github&#xf…...

怎么给三极管基极或者MOS管栅极接下拉电阻

文章是瑞生网转载&#xff0c;PDF格式文章下载&#xff1a; 怎么给三极管基极或者MOS管栅极接下拉电阻.pdf: https://url83.ctfile.com/f/45573183-1247189078-52e27b?p7526 (访问密码: 7526)...

Java Web学习笔记5——基础标签和样式

<!DOCTYPE html> html有很多版本&#xff0c;那我们应该告诉用户和浏览器我们现在使用的是HMTL哪个版本。 声明为HTML5文档。 字符集&#xff1a; UTF-8&#xff1a;现在最常用的字符编码方式。 GB2312&#xff1a;简体中文 BIG5&#xff1a;繁体中文、港澳台等方式…...

01_深度学习基础知识

1. 感知机 感知机通常情况下指单层的人工神经网络,其结构与 MP 模型类似(按照生物神经元的结构和工作原理造出来的一个抽象和简化了模型,也称为神经网络的一个处理单元) 假设由一个 n 维的单层感知机,则: x 1 x_1 x1​ 至 x n x_n xn​ 为 n 维输入向量的各个分量w 1 j…...

60、最大公约数

最大公约数 题目描述 给定n对正整数ai,bi&#xff0c;请你求出每对数的最大公约数。 输入格式 第一行包含整数n。 接下来n行&#xff0c;每行包含一个整数对ai,bi。 输出格式 输出共n行&#xff0c;每行输出一个整数对的最大公约数。 数据范围 1 ≤ n ≤ 1 0 5 , 1≤n≤…...

设计模式在芯片验证中的应用——迭代器

一、迭代器设计模式 迭代器设计模式(iterator)是一种行为设计模式&#xff0c; 让你能在不暴露集合底层表现形式 &#xff08;列表、 栈和树等数据结构&#xff09; 的情况下遍历集合中所有的元素。 在验证环境中的checker会收集各个monitor上送过来的transactions&#xff0…...

imx6ull - 制作烧录SD卡

1、参考NXP官方的手册《i.MX_Linux_Users_Guide.pdf》的这一章节&#xff1a; 1、SD卡分区 提示&#xff1a;我们常用的SD卡一个扇区的大小是512字节。 先说一下i.MX6ULL使用SD卡启动时的分区情况&#xff0c;NXP官方给的镜像布局结构如下所示&#xff1a; 可以看到&#xff0c…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

vue3+vite项目中使用.env文件环境变量方法

vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量&#xff0c;这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

Python 高效图像帧提取与视频编码:实战指南

Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!

目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...

【Linux】Linux安装并配置RabbitMQ

目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的&#xff0c;需要先安…...