异常c/c++

目录

1.c语言传统处理错误方式

1、终止程序

2、返回错误码

2.c++异常概念

3.异常的使用

 3.1异常的抛出与捕获

3.2异常安全(还有一些异常重新抛出)

3.3异常规范

4.自定义异常体系

5.c++标准库的异常体系

6.异常优缺点

 1、优点

2、缺点

7、补充


1.c语言传统处理错误方式

1、终止程序

如assert断言、内存错误(原因有很多,越界访问啊等等)、除0错误

2、返回错误码

错误码,常见的就是程序运行后,如果没有错误的话,就是返回0,不正常的时候返回负的多少或者其他奇怪的数字。很多库的接口函数都把错误码放到errno中,表示错误。遇到这种,错误的原因需要查表或者自行寻找到错误原因。

2.c++异常概念

 异常在很多面向对象的语言都有,python也有。

异常是一种处理错误的方式,可以理解为程度比assert轻一点,处理方式不是粗暴的终止程序。当出现某个编译器无法处理的错误时,可以将其作为异常抛出,由程序员预先设置的程序来处理这个错误。

throw:抛出异常,“throw 字符串或数字”

catch:捕获异常,比如捕获到了字符串或数字,就会进入对应的块域中,执行设定的语句。可以多个catch捕获多个异常(比如针对字符串的、针对数字的)

try:在try里面的语句,都会在运行时进行检查,如果遇到异常,把异常抛出后,会马上跳到catch中。

try
{// 保护的标识代码
}catch( ExceptionName e1 )
{// catch 块
}catch( ExceptionName e2 )
{// catch 块
}catch( ExceptionName eN )
{// catch 块
}
void fa() {int a; cin >> a;if (a == 2){throw "wdawd";}else if (a == 3){throw 4;}else if (a == 4){string s("dwwdawda");throw s;}else if (a == 5){stack<int>s;s.push(2);throw s;}
}int main()
{try {fa();}catch (const char* err){cout << err << endl;}catch (int x){cout << x << endl;}catch (const string& es){cout << es << endl;}catch (...) {cout << 231231 << endl;}return 0;
}

如果没有遇到异常,在执行完try里面的语句后,会直接跳过catch里面的内容。

注意,如果是库里的对象返回的话,建议用const 类型&来接受,因为这样如果这个对象有移动拷贝的话,这个写法可以接受右值也可以接受左值,提高效率。

3.异常的使用

 3.1异常的抛出与捕获

相比c语言传统的错误码(遇到错误,要层层返回,每层都要处理,直到返回main函数),异常可以直接跳回到匹配的catch处。这个过程中,跳过的函数都是会正常销毁的

异常抛出和匹配原则

1、异常是通过抛出对象引出的,该对象的类型决定了应该激活哪个catch

2、被选中的处理代码是调用链(就是从main到第一个函数到其内的函数再到其内的函数,一路下去)中与对象类型匹配且距离抛出异常的位置最近的,比如在第一个函数处也有try,然后执行try里面的内容时抛了异常,并且catch的内容与抛出的对象的类型是匹配的,那就会执行第一个函数处的对应catch而不是main函数处的catch。

3、catch(...)可以捕获任意类型的异常,问题是不知道异常是什么   用来兜底的,建议一定加

4、抛出异常后,对于相应的对象,会生成该对象的拷贝,因为这个对象可能是个临时对象(比如局部变量),这个拷贝出来的对象,在catch之后会销毁。(效率还是很高的,因为临时对象的话,走移动拷贝,效率挺高的)。比如我们抛出了一个string对象,就上面的代码中一样。

5、类型不一定严格匹配,可以抛出派生类对象,让基类捕获,利用多态。具体看下面的异常体系

在函数调用链中异常栈展开匹配原则

1、throw抛出后,要确认自己是否在try里面,在的话才去找匹配的catch,然后跳到匹配的catch处

2、没有匹配的catch,就跳出当前的函数栈,在调用该函数的函数栈中继续找匹配的catch

3、如果在main函数中都没有找到匹配的catch就会终止程序。而整个沿着调用链向上找匹配的catch的过程,就是栈展开。所以实际中,我们都会加个catch(...),否则有异常没捕获就会直接终止程序。

4、在匹配的catch处执行完对应的语句后,会沿着当前的try ....catch之后的语句继续执行

3.2异常安全(还有一些异常重新抛出)

异常安全的核心问题就是在调用链非常长的情况下,异常会直接跳到catch,而这个过程很可能横跨了很多个函数。

1、不要在构造函数里抛异常,因为抛异常会直接跳到catch。如果构造函数里面要进行初始化,最典型的就是开辟多段空间给多个指针变量,如果这个过程中抛异常了,导致有些指针变量没有被初始化,仍然是野指针或空指针,那么析构函数在delete或者free的时候就会抛异常或者报错。简单点就是说可能会造成对象不完整或者没有完全初始化

2、析构函数不要抛异常。因为析构函数负责资源的清理,如果在析构函数中抛异常,很可能导致内存泄漏等问题,比如某个空间没有被delete掉。

3、异常经常会导致内存泄漏的问题。比如new和delete中间有一块抛出了异常,导致直接跳到catch了,delete就不会执行,这样就内存泄漏了,还会导致lock和unlock之间的死锁问题(这个是线程的知识,看我linux和网络的部分即可)

接下来是一种处理方法,利用异常重新抛出

void fa() {int a; cin >> a;if (a == 2){throw "wdawd";}else if (a == 3){throw 4;}else if (a == 4){string s("dwwdawda");throw s;}else if (a == 5){stack<int>s;s.push(2);throw s;}
}
void f() {int* x = new int;try {fa();}catch (...) {//不一定是...,只是最保险的就是这个,不管抛了什么异常都可以接受delete x;throw;//不管接受到什么异常,都通通重新抛出去。}
}
int main()
{try {f();}catch (const char* err){cout << err << endl;}catch (int x){cout << x << endl;}catch (const string& es){cout << es << endl;}catch (...) {cout << 231231 << endl;}return 0;
}

核心思路就是在关键位置提前捕获,然后再重新抛出

比较恶心的情况:

void f() {int* x = new int;int *x1,x2;try {x1 = new int;try {x2 = new int;}catch (...) {delete x1;delete x;throw;}}catch (...) {delete x;throw;}delete x;delete x1;delete x2;
}

这个方法写起来太麻烦了,可见这个方法也不是很好,依靠智能指针可以更加的高效处理这个问题

class SmartPtr {
public:SmartPtr(int *ptr):_ptr(ptr){}~SmartPtr() {delete _ptr;}
private:int* _ptr;
};
void f() {SmartPtr s1(new int);SmartPtr s2(new int);SmartPtr s3(new int);//这样就可以依靠析构函数自动清理资源了。//就算抛了异常,s1、s2、s3是局部变量,在出了作用域会自动销毁,调用它的析构函数}

3.3异常规范

出于上面的情况,98的时候,希望大家写异常的时候规范一些。

// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);
// 这里表示这个函数只会抛出A的异常
void* operator new (std::size_t size) throw (A);
// 这里表示这个函数不会抛出异常
void* operator delete (std::size_t size, void* ptr) throw();单单靠上面3个,还是会出问题,比如写了抛abc,但实际上可能抛了d。
又比如函数深层调用,一层调一层那岂不是每一层函数都要写好多的throw()。
而且因为考虑到兼容c语言的问题,这只能是个建议而不是规定。// C++11 中新增的noexcept,表示不会抛异常
thread() noexcept;
thread (thread&& x) noexcept;//通过这个简化,只标记不会抛异常的函数。
但要小心的是,间接层加了noexcept,但这层函数调用的函数抛了异常,是不会检测到的。而且一旦检测到抛出了规定外的异常,编译器也只是报错。

4.自定义异常体系

在实际过程中,大家都是设计一个异常体系,否则的下不同组各自管自己抛异常,最后害惨了外面管异常的,因为catch(...)是不确定究竟是什么异常,所以通过设计异常体系,让异常的信息更加的准确,方便处理。

 接下来是一个比较常见的异常体系

class Exception
{
public:Exception(const string& errmsg, int id):_errmsg(errmsg), _id(id){}virtual string what() const{return _errmsg;   //返回错误描述  }
protected:string _errmsg;//错误描述,比如基础的错误信息,比如内存错误等等int _id; //错误id
};//这里会有一个类,比如说是数据库开发组的。
class SqlException : public Exception
{
public:SqlException(const string& errmsg, int id, const string& sql):Exception(errmsg, id), _sql(sql){}virtual string what() const{string str = "SqlException:";//表明自己是什么错误类的str += _errmsg;str += "->";str += _sql;return str;//再基础的错误信息基础上,加上自己的额外备注信息,让错误描述更加的准确}
private:const string _sql;
};//缓存组,具体做什么因人而异,主要是不同的组都会加上一串字符串,表示这个异常是
//哪个组负责的
class CacheException : public Exception
{
public:CacheException(const string& errmsg, int id):Exception(errmsg, id){}virtual string what() const{string str = "CacheException:";str += _errmsg;return str;}
};//网络组的,记录请求类型等等的
class HttpServerException : public Exception
{
public:HttpServerException(const string& errmsg, int id, const string& type):Exception(errmsg, id), _type(type){}virtual string what() const{string str = "HttpServerException:";str += _type;str += ":";str += _errmsg;return str;}
private:const string _type;
};

那么如何捕获呢?

catch (const Exception& e) // 这里捕获父类对象就可以{// 多态cout << e.what() << endl;}

利用继承的特性,用父类对象的引用来接受派生类或者父类本身。

然后利用虚函数的特性,在满足多态的情况下,如果接受的派生类,那么what调用的就是派生类的what,如果接受的是父类,就会调用父类的what。

5.c++标准库的异常体系

 c++标准库也是弄了一个异常体系。

图片出自菜鸟编程。

6.异常优缺点

 1、优点

1、相比传统的错误码,异常可以更加清晰的展现出错误的信息,甚至包含堆栈的调用信息,这样可以帮助更好的定位bug

2、错误码必须通过层层的retrun返回错误码,然后才能拿到错误码,相比之下异常可以直接拿到信息,跳过中间的过程。

3、很多第三方的库都包含了异常,使用的时候也得关注异常。

4、部分情况用异常更好,比如构造函数没有返回值,只能抛异常。比如at这个函数,返回什么很重要,但问题是返回什么都可能不是错误信息,反而可能是一个正常的值,但确实下标越界了。

2、缺点

1、异常会导致程序的执行流乱跳,会非常混乱,尤其是运行时的时候抛异常就会乱跳。导致调试跟踪以及分析程序的时候会非常困难。

2、异常有一定的性能开销,不过在现在的硬件下,基本可以忽略不计。

3、c++没有垃圾回收机制,资源需要自己管理,容易造成内存泄漏、死锁等异常安全问题,想要解决,建议是rall(智能指针的内容)。

4、标准库的异常体系定义的一般,导致所有人都是各自定义各自的,比较混乱。

5、异常需要尽量的规范使用,否则负责捕获异常处理异常的人会很痛苦。主要是,所有的异常继承自一个基类;函数是否抛异常、抛什么异常,都使用func() throw()的形式规范

7、补充

异常一旦发生,说明程序出现了非法的情况
程序中只要有异常,就必须处理。一旦有抛出异常,那么必须捕获,否则代码最后会崩溃。

基类的const类型引用,可以捕获所有的子类的异常对象
 

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

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

相关文章

ChatGPT 提示词框架

作为一个资深安卓开发工程师&#xff0c;我们在日常开发中经常会用到 ChatGPT 来提升开发效率&#xff0c;比如代码优化、bug 排查、生成单元测试等。 但要想真正发挥 ChatGPT 的潜力&#xff0c;我们需要掌握一些提示词&#xff08;Prompt&#xff09;的编写技巧&#xff0c;并…

面试基础---JVM 运行时数据区

深入理解 JVM 运行时数据区&#xff1a;从源码到实践 在现代互联网大厂的开发环境中&#xff0c;Java 依然是主流语言之一&#xff0c;而 Java 虚拟机&#xff08;JVM&#xff09;作为 Java 程序运行的基础&#xff0c;其性能和稳定性直接关系到应用的表现。因此&#xff0c;深…

PostgreSQL 查看数据库及表中数据占用空间大小

1、应用场景 场景1&#xff1a;查看数据库占用空间大小 SELECT pg_size_pretty(pg_database_size(database_name));场景2&#xff1a;查看每张表占用空间大小 SELECTtable_schema || . || table_name AS table,#仅表数据pg_size_pretty(pg_relation_size(table_schema || . …

c++中打印任意类型任意长度数组的各种方式

目录 一、代码 二、详细解释 1. print 函数模板 2. array_size 函数模板 3. print1 函数模板 4. print2 函数模板 5. my_begin 和 my_end 函数模板 6. print3 函数模板 7. main 函数 总结 一、代码 如下代码给出了5种方式打印任意类型任意长度的数组。这段代码定义了…

ubuntu下r8125网卡重启丢失修复案例一则

刚装的一台服务器&#xff0c;ubuntu24.04&#xff0c;主板网卡是r8125&#xff0c;安装服务后会莫名其妙丢失驱动 按照官网的方法下载最新8125驱动包&#xff1a; Realtek 然后卸载驱动 rmmod r8125 然后在驱动包里安装&#xff08;幸好我之前装了build-essential&#x…

[Python学习日记-84] 进程理论

[Python学习日记-84] 进程理论 简介 进程的概念 并发与并行的区别 进程并发的实现 简介 进程理论是计算机科学中一种重要的概念&#xff0c;用来描述操作系统中执行的程序实例。在操作系统中&#xff0c;每个程序的执行被称为一个进程。进程理论研究进程的创建、调度、通信…

云创智城YunCharge 新能源二轮、四轮充电解决方案(云快充、万马爱充、中电联、OCPP1.6J等多个私有单车、汽车充电协议)之新能源充电行业系统说明书

云创智城YunCharge 新能源充电行业系统说明书 ⚡官方文档 ⚡官网地址 1. 引言 随着全球环境保护和能源危机的加剧&#xff0c;新能源汽车行业得到了快速发展&#xff0c;充电基础设施建设也随之蓬勃发展。新能源充电行业系统旨在提供高效、便捷的充电服务&#xff0c;满足电…

OpenWebUI配置异常的外部模型导致页面无法打开

一、使用Ollama关闭OpenAI OpenWebUI自带OpenAI的API设置&#xff0c;且默认是打开的&#xff0c;默认情况下&#xff0c;启动后&#xff0c;会不断的去连https://api.openai.com/v1&#xff0c;但是无法连上&#xff0c;会报错&#xff0c;但是不会影响页面&#xff0c;能正常…

RuntimeWarning: invalid value encountered in scalar power在进行标量的幂运算时遇到了无效值

year_profit ((profit / initial_cash) ** (1 / yy) - 1) * 100 RuntimeWarning: invalid value encountered in scalar power 这个警告表示在执行标量幂运算 ((profit / initial_cash) ** (1 / yy) - 1) * 100 时遇到了无效值。常见的引发原因及解决办法如下&#xff1a; ###…

计算机毕业设计 ——jspssm506Springboot 的旧物置换网站

&#x1f4d8; 博主小档案&#xff1a; 花花&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 花花在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于java、python等技术。近年来&#xff0c;花花更…

Kafka消费者相关

Kafka生产者相关-CSDN博客 消费者消费数据基本流程 package com.hrui;import org.apache.kafka.clients.consumer.ConsumerConfig; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.ConsumerRecords; import org.apache…

【软考-架构】备战2025软考

新老教材对比 科目1&#xff08;信息系统综合&#xff09;考点详解 科目2&#xff08;系统架构设计案例&#xff09;考点详解 科目3&#xff08;系统架构设计论文&#xff09;考点详解 趋于越来越具体 学习方法推荐 第一阶段 – 基础知识阶段 建议一个半月&#xff1b; 先过…

MMW-1碳棒磨损机设计

摘 要 为了更好的测量在一定压力下碳棒的磨损量&#xff0c;提高碳棒磨损量的测量精度&#xff0c;本文设计了一种MMW-1碳棒磨损机&#xff0c;该碳棒磨损机属于柱盘式摩擦磨损试验机的一种。该机器主要用于做和碳棒有关的摩擦磨损试验&#xff0c;可以更准确的获得相关的参数…

网络运维学习笔记(DeepSeek优化版)005网工初级(HCIA-Datacom与CCNA-EI)链路层发现协议与VLAN技术

文章目录 一、链路层发现协议1.1 思科CDP协议1.2 华为LLDP协议 二、VLAN&#xff08;Virtual Local Area Network&#xff0c;虚拟局域网&#xff09;技术详解2.1 基本概念2.2 技术特性2.3 接口工作原理2.3.1 Access模式2.3.2 Trunk模式 2.4 厂商配置对比思科配置华为配置 2.5 …

SOME/IP-SD -- 协议英文原文讲解5

前言 SOME/IP协议越来越多的用于汽车电子行业中&#xff0c;关于协议详细完全的中文资料却没有&#xff0c;所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块&#xff1a; 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 5.1.2.5 S…

APNG格式图片文件大小优化方案 转WEBP

文章目录 原因过程相关下载相关文档后记 原因 页面上有个特效动画&#xff0c;PNG文件&#xff0c;APNG格式&#xff0c;13M大小&#xff0c;太占用内容了&#xff0c;要优化一下。 过程 直接上命令吧 ffmpeg -i input.apng -vf "formatrgba" -loop 0 output.web…

个人电脑小参数GPT预训练、SFT、RLHF、蒸馏、CoT、Lora过程实践——MiniMind图文版教程

最近看到Github上开源了一个小模型的repo&#xff0c;是真正拉低LLM的学习门槛&#xff0c;让每个人都能从理解每一行代码&#xff0c; 从零开始亲手训练一个极小的语言模型。开源地址&#xff1a; GitHub - jingyaogong/minimind: &#x1f680;&#x1f680; 「大模型」2小时…

PHP Zip 文件处理指南

PHP Zip 文件处理指南 引言 ZIP文件是一种流行的压缩格式&#xff0c;广泛用于文件压缩和归档。PHP作为一门强大的服务器端脚本语言&#xff0c;提供了丰富的类和方法来处理ZIP文件。本文将详细介绍PHP中ZIP文件的处理方法&#xff0c;包括创建、添加文件、提取文件以及压缩和…

Java使用ZXing库生成带有Logo的二维码图片,并去除白边动态伸缩上传到阿里云OSS

文章目录 引言二维码基本原理1、二维码概述2、QR Code结构3、错误纠正级别 QR Code生成技术1、ZXing库2、生成二维码的步骤 图像处理技术1、嵌入Logo2. 去除白边 阿里云OSS基本概念1、OSS概述2. 主要功能3. 基本概念 实战演示1、依赖库2、类结构3、生成普通二维码4. 去除白边5、…

AI工具箱最新使用教程

先克隆项目 电脑需要先安装 git &#xff0c;安装的画看这个 Git安装教程&#xff08;超详细&#xff09;。 git镜像 git clone https://github.com/Escaflowne1985/MyToolsWebBackendUser.gitgitee镜像 git clone https://gitee.com/escaflowne/MyToolsWebBackendUser.git…