C++关键字noexcept应用及案例

文章目录

      • 使用场景:
      • 注意事项:
    • `noexcept`在C++中的应用和重要性:
      • 与标准库的交互
      • 与异常安全相关的编程模式
      • 与C++标准的关系
      • 与性能的关系
      • 示例代码
    • 综合案例
      • 扩展后的代码
      • 新增功能解释
      • 异常安全
      • 性能优化

在C++中, noexcept是一个关键字,用于指定一个表达式或函数在执行时不会抛出异常。这可以用来描述函数的行为,即该函数的执行保证不会因为内部错误而抛出异常,这对于优化以及在某些情况下确保代码的正确性非常有用。

使用场景:

  1. 异常规格说明

    • 在函数声明或定义后面加上noexcept关键字(不带括号),表示这个函数不会抛出任何异常。例如:
      void myFunction() noexcept;
      
  2. 异常规格说明(带有条件)

    • 可以使用noexcept(expression)形式来指定一个表达式,如果该表达式的值为true,则函数不会抛出异常。例如:
      void myFunction(int x) noexcept(x > 0);
      
      这里x > 0是一个布尔表达式,如果为真,则函数被标记为不会抛出异常。
  3. 类型推导

    • 在一些上下文中,如返回类型推导,可以使用noexcept来帮助确定模板函数的返回类型是否包含异常规范。

注意事项:

  • noexcept并不意味着函数内部不能包含可能抛出异常的操作;而是说,即使有异常发生,也会被适当地处理,不会传播到函数调用者那里。
  • noexcept可以用来优化性能,编译器可以假设函数不会抛出异常,从而避免一些检查或处理异常的相关开销。
  • 如果一个标为noexcept的函数实际上抛出了异常,并且没有适当的处理(如捕获异常),那么行为是未定义的。

在设计系统时合理地使用noexcept可以帮助提高系统的健壮性和可预测性。特别是在需要关心资源管理(如析构函数)或者在某些必须保证不抛出异常的情况下(如C++标准库中的swap函数),noexcept的使用就显得尤为重要。

noexcept在C++中的应用和重要性:

与标准库的交互

C++标准库中的一些组件会利用noexcept特性来提供更安全和高效的接口。例如,容器的swap方法通常被定义为noexcept,这意味着它们在交换两个对象的状态时不会抛出异常,这对于实现高效且安全的算法非常重要。

与异常安全相关的编程模式

当设计类和函数时,考虑其异常安全性是非常重要的。noexcept可以帮助你明确地表明某个函数是无异常的,这有助于其他开发者理解该函数的行为,并允许他们更安全地使用它。此外,在设计具有严格资源管理要求的对象时,确保析构函数是noexcept的可以防止资源泄漏。

与C++标准的关系

C++标准对于noexcept也有具体的要求。例如,C++标准要求某些操作(如基本类型的赋值和比较)必须是noexcept的,以便这些操作可以在不需要异常处理的环境中可靠地工作。

与性能的关系

虽然noexcept主要是一个关于正确性的特性,但它也可以对性能产生积极影响。编译器可以利用noexcept信息来进行优化,比如避免不必要的异常处理框架的设置。这对于性能敏感的应用程序来说尤其重要。

示例代码

下面是一个简单的示例,展示了如何使用noexcept来编写一个安全的交换函数:

#include <iostream>class MyClass {
public:int data;// 构造函数MyClass(int d) : data(d) {}// 移动构造函数MyClass(MyClass&& other) noexcept : data(other.data) {other.data = 0; // 重置other的数据}// 交换成员函数void swap(MyClass& other) noexcept {std::swap(data, other.data);}
};// 全局交换函数
void swap(MyClass& a, MyClass& b) noexcept {a.swap(b);
}int main() {MyClass a(10);MyClass b(20);std::cout << "Before swap: a=" << a.data << ", b=" << b.data << std::endl;swap(a, b); // 调用全局交换函数std::cout << "After swap: a=" << a.data << ", b=" << b.data << std::endl;return 0;
}

在这个例子中,swap函数被声明为noexcept,表明它不会抛出异常。此外,我们还提供了一个移动构造函数,它也是noexcept的,这样在需要移动语义的地方可以安全地使用这个类。

总的来说,noexcept是C++中一个非常有用的特性,它不仅有助于提高代码的安全性,还能在一定程度上提升性能。在编写新代码时考虑使用noexcept,并在重构旧代码时检查是否可以添加noexcept规格,都是很好的实践。

综合案例

下面是一个综合运用noexcept特性的示例代码。我们将创建一个简单的类MyVector,它类似于std::vector,但为了简化,只支持整数元素。这个类将展示如何使用noexcept来标记那些可以保证不会抛出异常的方法,同时也会展示如何处理潜在的异常情况。

#include <iostream>
#include <stdexcept> // For std::bad_alloc
#include <cstdlib>   // For std::exitclass MyVector {
private:int* data; // Pointer to the first element of the arraysize_t capacity; // Capacity of the arraysize_t size; // Number of elements in the vector// Helper function to allocate memory for the arrayvoid allocateMemory(size_t newCapacity) noexcept(false) {try {int* newData = new int[newCapacity];for (size_t i = 0; i < size; ++i) {newData[i] = data[i];}delete[] data;data = newData;capacity = newCapacity;} catch (std::bad_alloc&) {// Re-throw the exception after cleaning updelete[] data;throw;}}public:// Default constructorMyVector() noexcept : data(nullptr), capacity(0), size(0) {}// Constructor with initial capacityexplicit MyVector(size_t initial_capacity) noexcept(std::is_nothrow_constructible_v<int>) :data(new int[initial_capacity]), capacity(initial_capacity), size(0) {}// Destructor~MyVector() noexcept {delete[] data;}// Copy constructorMyVector(const MyVector& other) noexcept(std::is_nothrow_constructible_v<int>) :data(new int[other.capacity]), capacity(other.capacity), size(other.size) {for (size_t i = 0; i < size; ++i) {data[i] = other.data[i];}}// Move constructorMyVector(MyVector&& other) noexcept : data(other.data), capacity(other.capacity), size(other.size) {other.data = nullptr;other.capacity = 0;other.size = 0;}// Assignment operatorMyVector& operator=(const MyVector& other) noexcept(std::is_nothrow_constructible_v<int>) {if (this != &other) {if (capacity != other.capacity) {allocateMemory(other.capacity);}for (size_t i = 0; i < other.size; ++i) {data[i] = other.data[i];}size = other.size;}return *this;}// Move assignment operatorMyVector& operator=(MyVector&& other) noexcept {if (this != &other) {delete[] data;data = other.data;capacity = other.capacity;size = other.size;other.data = nullptr;other.capacity = 0;other.size = 0;}return *this;}// Add an element to the end of the vectorvoid push_back(int value) noexcept(false) {if (size == capacity) {// Double the capacity if it is not enoughallocateMemory(capacity ? 2 * capacity : 1);}data[size++] = value;}// Get the size of the vectorsize_t getSize() const noexcept {return size;}// Access operatorint& operator[](size_t index) noexcept(index < size) {return data[index];}// Const access operatorint operator[](size_t index) const noexcept(index < size) {return data[index];}
};int main() {try {MyVector vec(5); // Initialize vector with capacity 5vec.push_back(1);vec.push_back(2);vec.push_back(3);vec.push_back(4);vec.push_back(5);vec.push_back(6); // This will trigger a reallocationstd::cout << "Size: " << vec.getSize() << std::endl;for (size_t i = 0; i < vec.getSize(); ++i) {std::cout << vec[i] << " ";}std::cout << std::endl;} catch (const std::exception& e) {std::cerr << "Exception caught: " << e.what() << std::endl;std::exit(EXIT_FAILURE);}return 0;
}

在这个示例中:

  • allocateMemory方法可能会抛出异常(如果内存分配失败),因此它的noexcept属性为false
  • 构造函数、析构函数、复制构造函数、移动构造函数、复制赋值运算符和移动赋值运算符都标记了noexcept属性,根据它们的实际行为来确定是否真的不会抛出异常。
  • push_back方法也可能抛出异常(如果内存分配失败),因此它的noexcept属性同样为false
  • 访问运算符operator[]被标记为noexcept,因为它不会抛出异常(前提是索引在范围内)。

这个示例展示了如何在类的设计中合理使用noexcept来明确指出哪些方法可以保证不会抛出异常,以及如何处理那些可能会抛出异常的情况。

我们继续扩展之前的示例,增加更多功能,并确保正确处理异常情况。这次我们将增加一个resize方法,并且确保所有必要的资源管理操作都是安全的。此外,我们还将展示如何在异常处理中使用noexcept来确保析构函数的健壮性。

扩展后的代码

#include <iostream>
#include <stdexcept> // For std::bad_alloc
#include <cassert>   // For assert
#include <cstdlib>   // For std::exitclass MyVector {
private:int* data; // Pointer to the first element of the arraysize_t capacity; // Capacity of the arraysize_t size; // Number of elements in the vector// Helper function to allocate memory for the arrayvoid allocateMemory(size_t newCapacity) noexcept(false) {try {int* newData = new int[newCapacity];for (size_t i = 0; i < size; ++i) {newData[i] = data[i];}delete[] data;data = newData;capacity = newCapacity;} catch (std::bad_alloc&) {// Re-throw the exception after cleaning updelete[] data;throw;}}// Helper function to resize the vectorvoid resize(size_t newSize) noexcept(false) {if (newSize > capacity) {// Increase the capacity to at least the new sizesize_t newCapacity = capacity ? 2 * capacity : 1;while (newCapacity < newSize) {newCapacity *= 2;}allocateMemory(newCapacity);}if (newSize > size) {// Fill the remaining elements with default values (zero)for (size_t i = size; i < newSize; ++i) {data[i] = 0;}}size = newSize;}public:// Default constructorMyVector() noexcept : data(nullptr), capacity(0), size(0) {}// Constructor with initial capacityexplicit MyVector(size_t initial_capacity) noexcept(std::is_nothrow_constructible_v<int>) :data(new int[initial_capacity]), capacity(initial_capacity), size(0) {}// Destructor~MyVector() noexcept {delete[] data;}// Copy constructorMyVector(const MyVector& other) noexcept(std::is_nothrow_constructible_v<int>) :data(new int[other.capacity]), capacity(other.capacity), size(other.size) {for (size_t i = 0; i < size; ++i) {data[i] = other.data[i];}}// Move constructorMyVector(MyVector&& other) noexcept : data(other.data), capacity(other.capacity), size(other.size) {other.data = nullptr;other.capacity = 0;other.size = 0;}// Assignment operatorMyVector& operator=(const MyVector& other) noexcept(std::is_nothrow_constructible_v<int>) {if (this != &other) {if (capacity != other.capacity) {allocateMemory(other.capacity);}for (size_t i = 0; i < other.size; ++i) {data[i] = other.data[i];}size = other.size;}return *this;}// Move assignment operatorMyVector& operator=(MyVector&& other) noexcept {if (this != &other) {delete[] data;data = other.data;capacity = other.capacity;size = other.size;other.data = nullptr;other.capacity = 0;other.size = 0;}return *this;}// Add an element to the end of the vectorvoid push_back(int value) noexcept(false) {if (size == capacity) {// Double the capacity if it is not enoughallocateMemory(capacity ? 2 * capacity : 1);}data[size++] = value;}// Resize the vector to a given sizevoid resize(size_t newSize) noexcept(false) {this->resize(newSize);}// Get the size of the vectorsize_t getSize() const noexcept {return size;}// Access operatorint& operator[](size_t index) noexcept(index < size) {return data[index];}// Const access operatorint operator[](size_t index) const noexcept(index < size) {return data[index];}
};int main() {try {MyVector vec(5); // Initialize vector with capacity 5vec.push_back(1);vec.push_back(2);vec.push_back(3);vec.push_back(4);vec.push_back(5);vec.push_back(6); // This will trigger a reallocationstd::cout << "Size before resize: " << vec.getSize() << std::endl;for (size_t i = 0; i < vec.getSize(); ++i) {std::cout << vec[i] << " ";}std::cout << std::endl;vec.resize(8); // Resize vector to capacity 8std::cout << "Size after resize: " << vec.getSize() << std::endl;for (size_t i = 0; i < vec.getSize(); ++i) {std::cout << vec[i] << " ";}std::cout << std::endl;} catch (const std::exception& e) {std::cerr << "Exception caught: " << e.what() << std::endl;std::exit(EXIT_FAILURE);}return 0;
}

新增功能解释

在这个扩展版本中,我们新增了一个resize方法,它可以改变向量的大小,并且如果新的大小超过了当前容量,它会自动调整容量。我们通过allocateMemory方法来完成内存重新分配的工作,并且在分配失败时,我们清理已有的资源并重新抛出异常,以确保资源不会泄露。

异常安全

  • 构造函数和析构函数:构造函数和析构函数都进行了适当的资源管理,确保不会抛出异常。
  • 拷贝构造函数和赋值操作:这些操作也进行了适当的资源管理,确保在拷贝过程中不会出现内存泄露等问题。
  • 移动构造函数和移动赋值操作:使用了 C++11 的右值引用,使得移动语义可以生效,提高了性能并且减少了内存占用。
  • push_backresize 方法:这两个方法可能会抛出异常(如果内存分配失败),因此它们的 noexcept 属性为 false

性能优化

通过使用 noexcept,编译器可以在适当的情况下进行优化,例如,在调用已知不会抛出异常的方法时,可以省略异常处理相关的开销。此外,使用移动语义可以减少临时对象的创建,提高程序的运行效率。

通过这个综合示例,我们可以看到如何在设计类时考虑异常安全性和性能优化,并且如何使用 noexcept 来帮助编译器更好地理解代码的行为。

😍😍 海量H5小游戏、微信小游戏、Web casualgame源码😍😍
😍😍试玩地址: https://www.bojiogame.sg😍😍
😍看上哪一款,需要源码的csdn私信我😍

————————————————

​最后我们放松一下眼睛
在这里插入图片描述

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

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

相关文章

STM32F103HAL库实现低功耗(睡眠模式、停止模式和待机模式)

STM32F103HAL库实现低功耗&#xff08;睡眠模式、停止模式和待机模式&#xff09; 1. STM32电源结构2. 电源管理器2.1 上电复位和掉电复位2.2 可编辑电压监测器&#xff08;PVD&#xff09; 3. 低功耗模式介绍3.1 睡眠模式3.2 停止模式3.3 待机模式 4. 低功耗相关寄存器5. 低功…

Windows: 如何实现CLIPTokenizer.from_pretrained`本地加载`stable-diffusion-2-1-base`

参考&#xff1a;https://blog.csdn.net/qq_38423499/article/details/137158458 https://github.com/VinAIResearch/Anti-DreamBooth?tabreadme-ov-file 联网下载没有问题&#xff1a; import osos.environ["HF_ENDPOINT"] "https://hf-mirror.com" i…

【vue】14.插槽:构建可复用组件的关键

今天看代码的时候碰到了插槽&#xff0c;有些看不懂&#xff0c;所以写下这篇文章&#xff0c;系统地梳理一下关于插槽的内容&#xff0c;也希望给大家带来一些帮助。 // 我碰到的插槽长这样 <template #default"scope">... </template> 一.什么是插槽…

camera和lidar外参标定

雷达和相机的外参标定&#xff08;外部参数标定&#xff09;指的是确定两者之间的旋转和平移关系&#xff0c;使得它们的坐标系可以对齐。 文章目录 无目标标定livox_camera_calibdirect_visual_lidar_calibration 有目标标定velo2cam_calibration 无目标标定 livox_camera_ca…

数据结构和算法-动态规划(3)-经典问题

动态规划常见问题 打家劫舍 题目 [力扣198] 198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 题目描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&…

深入理解Redis的四种模式

Redis是一个内存数据存储系统&#xff0c;支持多种不同的部署模式。以下是Redis的四种主要部署模式。 1、单机模式 单机模式是最简单的部署模式&#xff0c;Redis将数据存储在单个节点上。这个节点包括一个Redis进程和一个持久化存储。单机模式非常适合小型应用程序或者开发和…

Flutter实战短视频课程

1、课程导学 一套代研运行多蜡 体州一致&#xff0c;目胜能优昇 未来大趋势 不改交原生项目的基础上&#xff0c;扩展Flutter能力 Flutter原生灵话切涣 0入侵 最简单、最通用 最新Flutter 3,x新特性讲解 大量flutter官方组件和api学习 最常用的第三方库使用及原理解析 自研组…

消息队列-Rabbitmq(消息发送,消息接收)

将来我们开发业务功能的时候&#xff0c;肯定不会在控制台收发消息&#xff0c;而是应该基于编程的方式。由于RabbitMQ采用了AMQP协议&#xff0c;因此它具备跨语言的特性。任何语言只要遵循AMQP协议收发消息&#xff0c;都可以与RabbitMQ交互。并且RabbitMQ官方也提供了各种不…

QT相机连接与拍照

先看效果 编辑:已添加虚拟键盘辅助输入,添加二维码识别,用的QZxing 初始化 auto mainLayout = new QHBoxLayout(this);m_viewfinder = new QCameraViewfinder(this);m_viewfinder->setStyleSheet("border-radius: 20px;background-color:rgb(43,48,70)");mainL…

ubuntu openmpi安装(超简单)

openmpi安装 apt update apt install openmpi-bin openmpi-common libopenmpi-dev安装到此完毕 测试一下&#xff0c;success !

【C++】string 类深度解析:探秘字符串操作的核心

快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 目录 &#x1f4af;前言 &#x1f4af;为什么要学习 string 类 &#xff08;一&#xff09;简化操作 &#xff08;二&#xff09;确保安全 &#xff08;三…

【EndNote版】如何在Word中引用文献

1、在Word中&#xff0c;鼠标光标放在所需插入文献的位置 2、点击选项卡中的“EndNote X9”&#xff0c;直接在EndNote中选中对应的文献 3、选中文献&#xff0c;点击工具栏中的“引用” 4、最后就可在Word中看到所插入的文献

[面试题]ES6 Javascript

ES6 箭头函数和普通函数有什么区别? 1)定义方式:箭头函数使用箭头(>)语法&#xff0c;省略了 function 关键字。 2)参数处理:如果只有一个参数&#xff0c;箭头函数可以省略括号。 3)函数体:如果函数体只有一条语句&#xff0c;箭头函数可以省略花括号和 return 关键字 4)…

Leetcode 二叉树中的最大路径和

算法思想 这道题要求在一棵二叉树中找到路径和最大的路径。路径可以从树中任意一个节点开始&#xff0c;到任意一个节点结束&#xff0c;但路径上的节点必须是连续的。 算法使用递归的方式来遍历树中的每个节点&#xff0c;并在遍历过程中计算包含当前节点的最大路径和。具体…

计算机视觉实验一:图像基础处理

1. 图像的直方图均衡 1.1 实验目的与要求 (1)理解直方图均衡的原理与作用; (2)掌握统计图像直方图的方法; (3)掌握图像直方图均衡的方法。 1.2 实验原理及知识点 直方图均衡化是通过灰度变换将一幅图象转换为另一幅均衡直方图&#xff0c;即在每个灰度级上都具有相同的象素…

计算结构力学:多自由度振动系统

本文以笔记的形式记录计算结构力学的若干基础知识。 注1&#xff1a;限于研究水平&#xff0c;分析难免不当&#xff0c;欢迎批评指正。 注2&#xff1a;文章内容会不定期更新。 预修1&#xff1a;线性代数 1. 标准特征值 复矩阵Schur分解&#xff1a;对于复矩阵&#xff0c…

Linux基础环境搭建(CentOS7)- 安装Scala和Spark

#Linux基础环境搭建&#xff08;CentOS7&#xff09;- 安装Scala和Spark Linux基础环境搭建&#xff08;CentOS7&#xff09;- 安装Scala和Spark 大家注意以下的环境搭建版本号&#xff0c;如果版本不匹配有可能出现问题&#xff01;&#xff08;spark不要下2.4版本的 会报错…

Vue3使用AntV | X6绘制流程图:开箱即用

x6官方地址X6图编辑引擎 | AntV 官方文档仔细地介绍了很多丰富的功能,这里的demo可以满足基本的使用,具体拓展还需要仔细看文档内容 先上效果图 1、安装 通过 npm 或 yarn 命令安装 X6。 # npm npm install @antv/x6 --save# yarn yarn add @antv/x6 初始化画布 <di…

安装使用docker harbor并推送镜像到仓库

1.概要 通过上一章节的讲解&#xff0c;我们基本了解了docker的操作命令&#xff0c;在文章的最后我们成功的推送一个镜像到DockerHub的镜像仓库。从流程上说&#xff0c;操作过程可以说很完美&#xff0c;但是整个推送过程消耗的时间太长&#xff0c;我们消耗了大量时间在访问…

HTML练习题:彼岸的花(web)

展示效果: 代码: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>彼岸の花</title><style…