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

ops/2024/9/20 7:09:25/ 标签: 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.ppmy.cn/ops/15107.html

相关文章

Mongodb支持事务吗?

一、概念 1.1、MongoDB事务简介 MongoDB 是一个非关系型数据库管理系统&#xff0c;最初并不支持事务。然而&#xff0c;随着时间的推移&#xff0c;MongoDB 在其4.0版本中引入了多文档事务支持&#xff0c;使得在单个集合中执行多个操作成为可能。 In MongoDB, an operation…

C#面:阐述什么是泛型,泛型的优点有哪些?

泛型是 C# 中的一种特性&#xff0c;它允许我们编写可以在不同类型上工作的可重用代码。 通过使用泛型&#xff0c;我们可以编写更加灵活和通用的代码&#xff0c;而不需要为每种类型都编写重复的代码。 泛型的优点有以下几个方面&#xff1a; 代码重用&#xff1a;使用泛型可…

装饰器模式

一、实现原理 装饰器设计模式&#xff08;Decorator&#xff09;是一种结构型设计模式&#xff0c;它允许动态地为对象添加新的行为。它通过创建一个包装器来实现&#xff0c;即将对象放入一个装饰器类中&#xff0c;再将装饰器类放入另一个装饰器类中&#xff0c;以此类推&am…

nginx反向代理

简介 Nginx反向代理是一种服务器架构模式&#xff0c;它允许Nginx服务器接收客户端的请求&#xff0c;然后将这些请求转发到上游服务器&#xff08;例如应用服务器&#xff09;进行处理&#xff0c;并将处理后的响应返回给客户端。在这个过程中&#xff0c;Nginx充当了客户端和…

如何提交已暂存的更改到本地仓库?

文章目录 如何提交已暂存的更改到本地Git仓库&#xff1f;步骤1&#xff1a;确认并暂存更改步骤2&#xff1a;提交暂存的更改到本地仓库 如何提交已暂存的更改到本地Git仓库&#xff1f; 在Git版本控制系统中&#xff0c;当你对项目文件进行修改后&#xff0c;首先需要将这些更…

前端开发攻略---用原生JS在网页中也能实现 文本转语音!

1、原理 语音合成 (也被称作是文本转为语音&#xff0c;英语简写是 tts) 包括接收 app 中需要语音合成的文本&#xff0c;再在设备麦克风播放出来这两个过程。 Web API中对此有一个主要控制接口 SpeechSynthesis&#xff0c;外加一些处理如何表示要被合成的文本 (也被称为 utte…

(避雷指引:管理页面超时问题)windows下载安装RabbitMQ

一、背景&#xff1a; 学习RabbitMQ过程中&#xff0c;由于个人电脑性能问题&#xff0c;直接装在windows去使用RabbitMQ&#xff0c;根据各大网友教程&#xff0c;去下载安装完之后&#xff0c;使用web端进行简单的入门操作时&#xff0c;总是一直提示超时&#xff0c;要么容…

2024年华为OD机试真题-考勤信息-Python-OD统一考试(C卷D卷)

题目描述: 公司用一个字符串来表示员工的出勤信息: absent:缺勤 late:迟到 leaveearly:早退 present:正常上班 现需根据员工出勤信息,判断本次是否能获得出勤奖,能获得出勤奖的条件如下: 缺勤不超过一次;没有连续的迟到/早退;任意连续7次考勤,缺勤/迟到/早退不超过…

GaussDB轻量化运维管理工具介绍

前言 本期课程将从管理平台的架构出发&#xff0c;结合平台的实例管理、实例升级、容灾管理和监控告警的功能和操作介绍&#xff0c;全面覆盖日常运维操作&#xff0c;带您理解并熟练运用GaussDB运维平台完成运维工作。 一、GaussDB 运维管理平台简介 开放生态层 友好Web界面…

Python 基于docker部署的Mysql备份查询脚本

前言 此环境是基于docker部署的mysql&#xff0c;docker部署mysql可以参考如下链接&#xff1a; docker 部署服务案例-CSDN博客 颜色块文件 rootbogon:~ 2024-04-18 16:34:23# cat DefaultColor.py ######################################################################…

Python爬虫入门指南--爬虫技术的由来、发展与未来--实战课程大赠送

爬虫&#xff0c;也称为网络爬虫或网络蜘蛛&#xff0c;是一种自动化程序&#xff0c;专门用于遍历互联网并收集数据。这种技术的起源、发展和未来都与互联网紧密相连&#xff0c;并在信息检索、数据挖掘等多个领域发挥着不可或缺的作用。 "免费IP池大放送&#xff01;助…

我的读书摘记《点燃孩子的学习动力:关于儿童学习兴趣的真相》

德韦克认为乔丹的经历揭示了那些最卓越的学习者身上的一个秘密&#xff1a;人的天赋&#xff0c;是可以不断发展的&#xff01;不管早期的天赋如何&#xff0c;人终将不断超越自己&#xff0c;发展自己的天赋。 思维方式决定了学习的成功与否&#xff01;这也意味着&#xff0…

【CCF推荐-C类】计算机学术会议截稿信息2条

中国计算机学会推荐国际学术会议和期刊目录自2010年8月首次发布以来&#xff0c;已历经五版&#xff0c;得到了计算机领域科研工作者的广泛关注。 目录共包含十个领域&#xff0c;分为ABC三类&#xff1a;A类是国际上极少数顶会与顶刊&#xff1b;B类代表领域内高水平的会议与…

前端兼容 适配

一、浏览器 1、内核 浏览器内核优缺点IEtrident不安全Chromewebkit -> blink速度快&#xff0c;有兼容safriwebkit有兼容firefoxgecko功能强大 耗性能Operapresto -> blinkpresto 速度快&#xff0c;有兼容 2、浏览器兼容 1、html兼容 超链接访问过后hover样式不再出…

Redis中set数据类型详解

SADD key member [member …] summary: Add one or more members to a set since: 1.0.0 SCARD key summary: Get the number of members in a set since: 1.0.0 SDIFF key [key …] summary: Subtract multiple sets since: 1.0.0 SDIFFSTORE destination key [key …] sum…

mybatis的SqlSession

先来看一下sqlsession接口&#xff0c;发现它为我们定义了很多对数据库数据操作的相关方法。 public interface SqlSession extends Closeable {<T> T selectOne(String var1);<T> T selectOne(String var1, Object var2);<E> List<E> selectList(Str…

使用gogs搭建git服务器-注意事项

背景 自己的服务器上一直是用gitlab部署作为git服务,但是gitlab对机器的内存要求有点高,4G内存的小服务器开了gitlab就剩下几百兆内存了,别的服务器就受限制了,于是决定更换一个。 其实我对仓库管理系统要求没有太高,正常的上传下载浏览就行了。最后决定使用gogs。 踩坑 我…

Linux网络设置

将Linux主机接入到网络&#xff0c;需要配置网络相关设置 一般包括&#xff1a;主机名&#xff1b;IP/netmask&#xff1b;路由&#xff1a;默认网关&#xff1b;DNS服务器&#xff1a;主DNS服务器&#xff0c;次DNS服务器&#xff0c;第三个DNS服务器 网卡的配置文件在&…

EasyMR6.2 全面解读:四大功能深度优化,解锁全新大数据处理和计算体验

在刚刚过去的2024春季发布会上&#xff0c;袋鼠云带来了数栈产品V6.2版本的全新发布。其中&#xff0c;EasyMR 作为数栈V6.2中的一项关键能力&#xff0c;代表了袋鼠云对大数据生态的深入理解和持续创新。 EasyMR&#xff08;后文统称EMR&#xff09;是袋鼠云基于 Hadoop、Hiv…

JavaEE 初阶篇-深入了解 UDP 通信与 TCP 通信(综合案例:实现 TCP 通信群聊)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 UDP 通信 1.1 DatagramSocket 类 1.2 DatagramPacket 类 1.3 实现 UDP 通信&#xff08;一发一收&#xff09; 1.3.1 客户端的开发 1.3.2 服务端的开发 1.4 实现 …