C++高级特性:异常概念与处理机制(十四)

1、异常的基本概念
  • 异常:是指在程序运行的过程中发生的一些异常事件(如:除数为0,数组下标越界,栈溢出,访问非法内存等)

  • C++的异常机制相比C语言的异常处理:

    • 函数的返回值可以忽略,但异常不可以忽略(忽略异常程序会结束)
    • 整型返回值没有任何语义信息,而异常却包含语义信息,有时从类名中就能体现出来
    try{throw 异常值;
    } catch (异常类型1 异常值1) {} catch (异常类型2 异常值2) {} catch (异常类型3 异常值3) {} catch (...){      // 任何异常都可以捕获}
    
2、异常使用
  • 抛出异常位置后面的代码将没有机会执行,并且跳转到对应的捕获函数进行捕获
  • 通过不同类型的异常捕获进行处理,对于没有适合的捕获将会走默认的任意异常捕获
  • 如果没有任意异常捕获,并且向外抛出知道main也没有处理,那么程序将会直接结束!
2.1、异常的抛出与捕获
void test1()
{try {
//         throw 1;								// 
//        throw 'a';throw 3.14f;} catch (int e) {std::cout << "int 异常值为: " << e << std::endl;} catch (char e){std::cout << "char 异常值为: " << e << std::endl;} catch (...){std::cout << "默认异常处理" << std::endl;}
}
2.2、栈解旋

异常被抛出后,从进入try块起到异常抛出前(throw),这期间在栈上创建的所有对象都会被自动析构。析构的顺序与构造的顺序相反,这一过程被称为栈解旋。

void test2()
{try {A a1(10);A a2(20);A a3(30);throw 1;A a4(40);} catch (int e){std::cout << "int 异常值为: " << e << std::endl;} catch (char e){std::cout << "char 异常值为: " << e << std::endl;} catch (...){std::cout << "默认异常处理" << std::endl;}std::cout << "------------------" << std::endl;
}
/*
A(), m = 10
A(), m = 20
A(), m = 30
~A(), m = 30
~A(), m = 20
~A(), m = 10
int 异常值为: 1
------------------
*/
3、异常的接口声明
  • 异常的接口声明描述一个函数可以抛出哪些类型的异常
  • 为什么要抛出异常:有时候当前代码中不希望看到任何的异常处理,当遇到异常时向外抛出,并集中处理
3.1、函数默认

函数声明时不指定任何异常信息的描述,表示可以抛出任何异常

void test3()
{
//    throw 1;
//    throw 'a';throw "string";
}
3.2、抛出指定类型异常
  • 被指定只能抛出int和char类型的异常,抛出其他类型的异常将无法捕获
  • 但这种具体化可能抛出异常的类型写法在C++17中被移除了,C++17中推荐不写或者写noexcept(false)表示可能有异常抛出。
void func4() throw(int, char)    // noexcept(false)
{throw "hello";
}
void test4()
{try {func4();} catch (int e){std::cout << "int 异常值为: " << e << std::endl;} catch (char e){std::cout << "char 异常值为: " << e << std::endl;} catch (const char * e){std::cout << "string 异常值为: " << e << std::endl;} catch (...){std::cout << "默认异常处理" << std::endl;}std::cout << "------------------" << std::endl;
}
/*
terminate called after throwing an instance of 'char const*'
*/
3.3、不抛出任何异常
  • throw()表示不抛出任何异常,但是函数体内依然可以继续抛出,然而即使抛出外部捕获也无法处理
  • C++11之后推荐使用noexcept表示不抛出任何异常
void func5() throw()		// noexcept
{throw 1;
}
void test5()
{try {func5();} catch (int e){std::cout << "int 异常值为: " << e << std::endl;} catch (char e){std::cout << "char 异常值为: " << e << std::endl;} catch (const char * e){std::cout << "string 异常值为: " << e << std::endl;} catch (...){std::cout << "默认异常处理" << std::endl;}std::cout << "------------------" << std::endl;
}
/*
terminate called after throwing an instance of 'int'
*/
4、异常变量的生命周期
class MyException{
public:MyException(){std::cout << "异常变量构造函数" << std::endl;}MyException(const MyException& myException){std::cout << "拷贝构造函数" << std::endl;}~MyException(){std::cout << "析构函数" << std::endl;}
};
4.1、抛出普通变量异常

抛出普通变量异常会发生拷贝构造,可能会导致一部分的性能丢失

void Life_test1()
{try{throw MyException{};} catch (MyException e){std::cout << "----------" << std::endl;}
}
/*
异常变量构造函数
拷贝构造函数
----------
析构函数
析构函数
*/
4.2、抛出指针类型异常

抛出指针类型异常需要注意释放对象的内存空间,长期不释放会导致堆区空间告急

void Life_test2()
{try{throw new MyException();} catch (MyException* e){std::cout << "----------" << std::endl;delete e;}
}
/*
异常变量构造函数
----------
析构函数
*/
4.3、抛出引用类型异常

推荐使用抛出引用类型的异常处理,不会进行拷贝和手动分配堆区空间的问题,不会造成内存泄漏和性能负担

void Life_test3()
{try{throw MyException();} catch (MyException& e){std::cout << "----------" << std::endl;}
}
/*
异常变量构造函数
----------
析构函数
*/
5、异常的多态
  • 实际开发中应该会对所有的异常进行封装处理,不然catch语句太多太多无法处理,代码狮山

  • 这种情况就可以考虑使用多态的特性了,定义基类异常,所有可能的异常都继承基类异常,而捕获时只需要捕获基类异常即可

class BaseException{
public:virtual void printException(){std::cout << "BaseException" << std::endl;}
};class NullPointerException: public BaseException{
public:virtual void printException(){std::cout << "空指针异常!" << std::endl;}
};class OutOfRangeException: public BaseException{
public:virtual void printException(){std::cout << "越界访问异常!" << std::endl;}
};void polymorphism_exception_test1()
{try {
//        throw NullPointerException();                 // 输出: 空指针异常!throw OutOfRangeException();                    // 输出: 越界访问异常!} catch (BaseException& baseException){baseException.printException();}
}
6、C++标准异常

在这里插入图片描述

异常名称描述
exception所有标准异常的父类
bad_alloc当operator new和operator new[]请求分配内存失败时的异常
bad_exception这是个特殊的影响,如果函数的异常抛出列表里声明了bad_exception异常,当函数内部抛出了异常列表中没有的异常,这时调用的unexpected函数中若抛出异常,不论什么类型都会被替换为bad_exception类型
bad_typeid使用typeid操作符获取一个nullptr指针的类型,这时抛出bad_typeid异常
bad_cast使用dynamic_cast转换引用失败的时抛出
ios_base::failureio操作过程中出现错误
logic_error逻辑错误,可以在运行前检测的错误
runtime_error运行时错误,仅在运行时才可以检测的错误

logic_error子类

异常名称描述
length_error试图生成一个超过该类型最大长度的对象是,例如vector的resize操作
domain_error参数的阈值错误,主要用在数学函数中。例如使用一个负值调用只能操作非负数的函数
out_of_range超出有效范围
invalid_argument参数不合适。标准库中,当利用一个非’0’和’1’的string对象构造bitset时抛出这个异常

runtime_error子类

异常名称描述
range_error计算结果超出了有意义的值域范围
overflow_error算术计算上溢出
underflow_error算数计算下溢出
void standard_exception_test1()
{try {throw std::out_of_range("我越界了, 哈哈哈!");
//        throw std::bad_alloc();                         // 输出:std::bad_alloc
//        throw std::bad_cast();                          // 输出:std::bad_cast} catch (std::exception& e){std::cout << e.what() << std::endl;             // 我越界了, 哈哈哈!}
}
6.1、继承标准异常抛出
  • 这个函数在继承重写时需要加入noexcept或者对应的宏,防止子类异常抛出前被父类提前抛出
  • const表示这个函数只能读不能改
  • 当标准异常无法满足开发需求时,可以通过继承基类异常来编写自己的异常进行抛出,这样接口就统一了。
class NewException: public std::exception{
private:std::string msg;
public:NewException() :msg("我异常了!"){}explicit NewException(const std::string &msg) : msg(msg) {}virtual const char *what() const noexcept override {				// 需要加入noexceptreturn msg.c_str();}~NewException() {}
};void standard_exception_test2()
{try {throw NewException();} catch (std::exception& e){std::cout << e.what() << std::endl;									// 输出:我异常了!}
}

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

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

相关文章

《系统架构设计师教程(第2版)》第10章-软件架构的演化和维护-01-软件架构演化概述

文章目录 1. 演化的重要性2. 架构演化示例 教材中&#xff0c;本节名为&#xff1a;“软件架构演化和定义的关系” 1. 演化的重要性 演化目的&#xff1a;维持软件架构自身的有用性 为什么说&#xff0c;软件架构是演化来的&#xff0c;而不是设计来的&#xff1f; 软件架构的…

【LAMMPS学习】八、基础知识(4.3)TIP3P水模型

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

Python 基础 (Pandas):Pandas 入门

1. 官方文档 API reference — pandas 2.2.2 documentation 2. 准备知识&#xff1a;Pandas 数据结构 Series & DataFrame 2.1 Series 2.1.1 创建 Series 类型数据 一个 Series 对象包含两部分&#xff1a;值序列、标识符序列。可通过 .values (返回 NumPy ndarry 类型…

Fisher 准则分类

目录 一、什么是Fisher 准则 二、具体实例 三、代码实现 四、结果 一、什么是Fisher 准则 Fisher准则&#xff0c;即Fisher判别准则&#xff08;Fisher Discriminant Criterion&#xff09;&#xff0c;是统计学和机器学习中常用的一种分类方法&#xff0c;由统计学家罗纳…

C语言指针进阶:各类型指针变量详解

目录 1. 字符指针变量2. 数组指针变量2.1 什么是数组指针变量2.2 数组指针变量的初始化 3. 二维数组传参的本质4. 函数指针变量4.1 函数指针变量的创建4.2 函数指针变量的使用4.3 代码分析4.3.1 typedef 关键字 5. 函数指针数组6. 转移表 正文开始。 1. 字符指针变量 我们可以…

【WP】猿人学12_入门级js

https://match.yuanrenxue.cn/match/12 Fiddler分析发现&#xff0c;所有请求只是 page已经 m不一样 这个m看起来就很像 base64&#xff0c;解码发现确实如此 下面直接构建Python代码&#xff1a; import base64import requestsdef base64_encode_string(input_string):try:#…

搞嵌入式到底属于程序员吗?

搞嵌入式到底属不属于程序员呢&#xff1f;毫无疑问&#xff0c;当然算啊&#xff01;而且我十分赞同另一位朋友所说的&#xff1a;嵌入式程序员是难得的全栈型程序员。尽管嵌入式领域方向众多且繁杂&#xff0c;但他们同样也是会写代码的程序员。 嵌入式行业主要分为硬件和软…

LeetCode-219. 存在重复元素 II

题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;判断数组中是否存在两个 不同的索引 i 和 j &#xff0c;满足 nums[i] nums[j] 且 abs(i - j) < k 。如果存在&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&…

【写一个简单的service and client(C++)例子——services】

文章目录 1、概要2、引言3、服务开始的地方及步骤3.1 创建工作空间3.2 创建功能包3.3 更新package.xml3.4 编写service 节点3.4.1 添加可执行文件3.4.2 添加 install&#xff08;TARGETS…&#xff09; 部分 3.5 编写client 节点3.5.1 添加可执行文件 3.6 编译运行3.7 运行结果…

高端制造企业生产设备文件管理,怎样保证好用不丢失文件?

高端制造业在市场经济中占据重要角色&#xff0c;在高端制造业企业内部&#xff0c;生产设备又是最关键的一环环&#xff0c;它们不仅负责完成生产任务&#xff0c;同时也会产生大量的文件。这些数据反映了设备的运行状态、生产效率、能源消耗以及产品质量等多个方面&#xff0…

网络协议安全:OSI七层模型分层及作用,数据封装与解封过程,数据传输过程。

「作者简介」&#xff1a;2022年北京冬奥会中国代表队&#xff0c;CSDN Top100&#xff0c;学习更多干货&#xff0c;请关注专栏《网络安全自学教程》 这一章节我们需要知道OSI分哪七层&#xff0c;每层的作用&#xff0c;知道数据在七层模型中是怎样传输的&#xff0c;封包和解…

C/C++ 入门(7)vector类(STL)

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a;C 请多多指教&#xff01; 目录 一、标准库中的vector 1、了解 2、vector常用接口 二、vector的实现 1、框架 2、构造、析构函数 3、操作函数 三 、问题 1、由于赋值而引起的浅拷贝 2、因为类没…

岭回归(概念+实例)

目录 前言 一、基本概念 1. 引言 2. 岭回归的原理 3. 数学表达式 4. 岭回归的优点 5. 岭回归的局限性 6. 实际应用 二、具体实例 前言 “岭回归”这个词源于英文“Ridge Regression”&#xff0c;是一种用于处理回归分析中多重共线性&#xff08;multicollinearity&am…

Linux-软件安装--jdk安装

jdk安装 前言1、软件安装方式二进制发布包安装rpm安装yum安装源码编译安装 2、安装jdk2.1、使用finalShell自带的上传工具将jdk的二进制发布包上传到Linux2.2、解压安装包2.3、配置环境变量![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/61ba9750e2e34638a39575c5…

电脑自带dll修复在哪里打开?教你如何快速修复dll丢失问题

MSVCP140.dll文件作为Windows操作系统中不可或缺的一环&#xff0c;对众多基于C编译的应用程序的正常运行起着关键作用。在我深入研究和处理与该文件相关问题的过程中&#xff0c;积累了丰富的认知和实践经验。以下是我对MSVCP140.dll文件的总体介绍以及针对其丢失问题的解决方…

C++ 验证一下,你对递归是不是一知半解

公众号:编程驿站 公众号:编程驿站 1. 前言 无递归,不算法。无论怎样强调递归的重要性,都不为过。受限于计算机的思维能力,计算机的计算找答案的过程就是在不停试错、纠正错误的过程,类似于爱迪生发明灯炮。递归能帮助我们在不知道计算边界的情形下试错。 多函数求解过…

echarts树图-实现拓扑图效果

使用echarts树图来实现拓扑图效果&#xff0c;其效果如下&#xff1a; 代码如下&#xff1a; const data {name: XXX公司,children: [{name: 网络主机,children: [{name: 普通路由器,children: [{name: 智能网关},{name: 192.168.1.0/24}]}]},{name: 企业路由器},{name: 三…

MySQL-----多表查询(一)

目录 一.多表关系&#xff1a; 1.1 一对多(多对一)&#xff1a; 1.2 多对多: 1.3 一对一: 二.多表查询概述&#xff1a; 三.连接查询&#xff1a; 3.1内连接&#xff1a; 3.2外连接&#xff1a; 3.3自连接查询&#xff1a; 3.4联合查询&#xff1a; 一.多表关系&…

Vast+产品展厅 | Vastbase G100数据库是什么架构?(1)

Vastbase G100是海量数据融合了多年对各行业应用场景的深入理解&#xff0c;基于openGauss内核开发的企业级关系型数据库。 了解Vastbase G100的架构&#xff0c;可以帮助您确保数据库系统的高效、可靠和安全运行。 “Vast产品展厅”将分两期&#xff0c;为您详细讲解Vastbas…

划分数据集2,详细说明

看完了这个之后划分数据集&#xff0c;训练自己的数据集。-CSDN博客 我再详细说一下自己标注的文件放在什么位置 我发的文件里有这几个文件 在dataset里面有 自己的数据集分为&#xff0c;图片部分和标注文件部分 打开VOCdevkit文件夹 里面有三个文件夹 自己的图片的话&…