C++异常的规则

点击蓝字

c494f146daf91d0d23867b34571eed0f.png

关注我们

异常是指存在于程序运行时的异常行为,这些行为超出了函数正常功能的范围,当程序的某部分检测到一个无法处理的问题时,就需要用到异常处理。

1. C语言中传统的处理错误方式

终止程序:如assert,当发生错误时,直接终止程序,这样的作法不友好。

返回错误码:如果函数体里发生错误时,将错误码返回给coder,需要去查找对应的错误,系统的库函数接口就是通过把错误码放到errno中,表示错误。

Windows下,使用perror打印全部错误:

for (int i = 0; i < 43; ++i){cout << i << " :  ";perror(strerror(i));cout << endl;}

大部分情况下,C语言出现错误,都是使用的是返回错误码的方式处理,部分情况下使用终止程序来处理十分严重的错误。

2. C++中处理异常的方式

如果程序中含有可能引发异常的代码,那么通常也需要有专门的代码处理问题,如:程序的问题是输入无效,则异常处理部分可能会要求用户重新输入正确的数据。

异常处理机制为程序中异常检测和异常处理这两部分的协作提供支持,C++中,异常处理包括:

  1. throw:异常检测部分使用throw来表示它遇到了无法处理的问题,此时就会抛异常;

  2. catch:用于捕获异常,可以有多个catch同时进行捕获;

  3. try:try块中的代码抛出的异常通常会被一个或多个catch处理,因为catch处理异常,所以他们也被称为异常处理代码。

2.1 throw

程序的异常检测部分使用throw抛出一个异常,throw后紧跟一个表达式,该表达式的类型就是抛出的异常类型。

一般来说,直接将异常抛出,交给后面的程序处理异常,不应该将异常信息给直接输出。

int main()
{FILE* fp = fopen("a.txt", "a");//抛出异常的类型为string类型if (fp == nullptr)throw string("请检查文件是否存在");return 0;
}

2.2 try

try关键字后,紧跟着一个块,这个块中是花括号扩起来的语句序列,跟在try块之后的是一个或多个catch子句;

catch子句包括三部分:关键字catch、括号内的对象声明(异常声明,异常类型,抛出异常的类型要和catch处理的异常类型相同),一个处理异常的代码块;

当选中某个catch子句处理异常后,执行与之对应的块,catch一旦完成,程序跳转到try语句最后一个catch之后的语句继续执行。

int main()
{try{FILE* fp = fopen("a.txt", "r");// 以只读方式打开一个文件if (fp == nullptr)throw string("请检查文件是否存在");}catch (const char* msg)// char*类型异常{cout << msg << endl;}catch (const string& msg)//string类型异常{cout << msg << endl;}catch (...)//...  代表可以捕获任意类型的异常{cout << "出现了无法解决的异常" << endl;}return 0;
}

3. 异常抛出和捕获的规则

  1. 异常是通过抛出对象而引起的,该对象的类型决定了应该匹配哪个catch的处理代码;

  2. 异常处理部分catch,是调用链中与该类型匹配且抛出异常位置最近的那一个;

  3. 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的对象可能是一个临时对象,所以会生成一个拷贝对象,该拷贝的临时对象会在catch结束后销毁;

  4. catch(...)可捕获任意类型异常,代表了不知道出现异常的错误是什么,也就无法进行解决;

  5. 在异常的抛出和捕获中,并不是类型的完全匹配,可以抛出派生类对象,使用基类捕获(很重要)。

在函数调用链中异常栈展开的匹配规则:

  1. 先检查throw是否在try块内部,如果是则再查找匹配的catch语句,如果有匹配则调用catch的异常处理代码;

  2. 若没有匹配的catch异常处理代码,则退出当前函数栈,继续在调用函数栈中查找匹配catch;

  3. 如果达到main函数栈中,仍没有匹配,则终止程序,沿着调用栈查找匹配的catch子句的过程称为栈展开;所以一般情况下,都要在最后加一个catch(...)捕获任意类型的异常,否则当有异常没有被捕获时,就会导致程序终止;

  4. 找到匹配的catch子句后,会沿着catch之后的代码继续执行。

注意:

  1. throw可以抛出任意类型的异常,抛出的异常必须进行捕获,否则程序就会终止;

  2. throw抛出异常后,若是在多个函数栈中调用时,会直接跳转到有匹配的catch子句中,若没有匹配的子句时,程序终止;

  3. catch(...)可捕获任意类型异常。

 异常重新抛出

有可能单个的异常不能完全处理一个异常,则在进行一些处理后,希望再给外层的调用链函数来处理,catch则可以通过重新抛出异常将异常传递给更上层的函数处理;

double Div(int a, int b)
{if (b == 0)throw string("发生了除0错误");return a / b;
}void func()
{int *p = new int(10);int b = 0;cout << Div(*p, b);delete p;
}int main()
{try{func();}catch (const string& s){cout << s << endl;}return 0;
}

上述代码中,会出现内存泄漏,throw抛出的异常,直接跳转到main函数栈中,则会导致func中,申请的空间没有释放,造成内存泄漏,则需要对该异常进行重新捕获,并且释放该空间,避免内存泄漏。

这样修改就不会存在内存泄漏:

void func()
{int *p = new int(10);try{int b = 0;cout << Div(*p, b);}catch (...){delete p;throw;}delete p;
}

4. 异常安全

异常安全:异常导致的安全问题。

异常中断了程序的正常流程,异常发生时,调用者请求的一部分计算可能已经完成了,另一部可能还没完成。通常情况下,略过部分程序意味着某些对象处理到一般就戛然而止了,从而导致对象处于无效或未完成的状态,或者资源没有被正常释放。

那些在异常发生期间正确执行了“清理”工作的程序被称为异常安全的代码。

注意:

  1. 构造函数完成对象的构造和初始化,最好不要在构造函数中抛异常,否则可能导致对象不完整或者没有完全初始化;

  2. 析构函数主要完成资源的清理,最好不要在析构函数中抛出异常,否则可能导致资源泄漏;

  3. C++中经常会导致资源泄漏的问题,如new 和 delete中抛出异常,导致内存泄漏,lock和unlock之间抛出遗产,导致死锁,C++经常使用RAII来解决上述问题;

异常规范

  1. 异常规则说明说明的目的是为了让函数使用者知道该函数可能抛出什么异常,在函数后面接throw,列出这个函数可能抛出的所有异常类型;

void func() throw(string, char, char*);//可抛出三种类型的异常
void* operator new(size_t) throw(bad_alloc);//只会抛bad_alloc异常
  1. 函数后面接throw(),表示不会抛出异常;

void func() throw();//不抛异常
void* operator new(size_t) throw();//不抛异常
  1. 如果没有异常接口声明,则可以抛任意类型的异常。

5. 自定义异常体系

自定义异常的体系,一般情况下,抛出派生类的异常,由基类捕获,这样在不同的派生类中,可以抛出许多不同的异常,而且具有相同的调用方式(由基类调用),避免调用混乱,方便管理。

class Exception
{
public:Exception(const char* msg):_errmsg(msg){}virtual string what() = 0;//纯虚函数,接口类string _errmsg;
};class NetException : public Exception
{
public:NetException(const char* msg):Exception(msg){}virtual string what()
{return "网络错误" + _errmsg;}
};class SqlException : public Exception
{
public:SqlException(const char* msg):Exception(msg){}virtual string what()
{return "数据库错误" + _errmsg;}
};

那么在捕获的时候,只需要捕获基类的异常即可:

void Func()
{if (rand() % 33 == 0)throw SqlException("数据库启动出错");else if(rand() % 17 == 0)throw NetException("网络连接出错");
}int main()
{for (int i = 0; i < 188; ++i){try{Func();}catch (Exception& e)//捕获基类 即可{cout << e.what() << endl;}}return 0;
}

6. 异常优缺点

优点:

1.清晰的包含错误信息;

2.如果有越界问题时,可以很方便的处理;

3.多层调用时,里层发生错误,不会层层调用,最外层可直接捕获;

4.一些第三方库也是使用异常,使用异常时可以很方便使用这些库:如boost

缺点:

1.异常会导致执行流跳转,分析程序时会有一些问题;

2.C++中没有GC,异常可能会导致资源泄漏的风险;

3.C++库中定义的异常体系,可用性不高,一般自己定义;

4.C++可以抛任意类型的异常,则需要对异常最很好的规范管理,否则就会非常混乱,所以一般定义出继承体系下的异常规范。

a9f4dbdf0c223434e0acfa8b9f9a4822.gif

如果你年满18周岁以上,又觉得学【C语言】太难?想尝试其他编程语言,那么我推荐你学Python,现有价值499元Python零基础课程限时免费领取,限10个名额!
▲扫描二维码-免费领取

8d83769ad908b5d1f90844be4749e3b8.gif

戳“阅读原文”我们一起进步

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

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

相关文章

java中集合选取怎么选_集合中的可选

java中集合选取怎么选有时有人认为Optional类型值得在集合中使用。 据称&#xff0c;它解决了以下问题&#xff1a; HashMap在没有键映射以及值null映射到键的情况下返回null 。 如果使用Map<Optional<Something>>则可以清楚地区分缺少的映射和缺少的值。 这样一来…

【lua学习】7.环境

1 最重要的两个数据结构1.1 lua_State(Lua虚拟机/Lua协程)1.2 global_State(Lua全局状态)2 环境相关的变量2.1 Global表2.1.1 Global表在lua_State结构中2.1.2 Global表在 f_luaopen 时被初始化2.2 env表2.2.1 env表在Closure结构中2.2.2 查找一个全局变量<>在当前函数的…

异质性查询需要为连线设定_振奋人心!华东理工大学开发新型的荧光染料,为细胞成像奠定基础...

结合并激活荧光染料的适体荧光RNA(FR)已用于对丰富的细胞RNA种类进行成像。然而&#xff0c;诸如低亮度和具有不同光谱特性的染料/适体组合的有限可用性的局限性&#xff0c;限制了这些工具在活的哺乳动物细胞和体内的使用。最近&#xff0c;华东理工大学朱麟勇及杨弋共同通讯在…

C++ STL详解(1)

点击蓝字关注我们概述STL 是“Standard Template Library”的缩写&#xff0c;中文译为“标准模板库”。STL 是 C 标准库的一部分&#xff0c;不用单独安装。C 对模板&#xff08;Template&#xff09;支持得很好&#xff0c;STL 就是借助模板把常用的数据结构及其算法都实现了…

jooq 配置oracle_jOOQ配置

jooq 配置oracle本文是我们学院课程“ jOOQ –类型安全数据库查询”的一部分 。 在SQL和特定关系数据库很重要的Java应用程序中&#xff0c;jOOQ是一个不错的选择。 当JPA / Hibernate抽象过多而JDBC过于抽象时&#xff0c;这是一种替代方法。 它显示了一种现代的领域特定语言…

各种说明方法的答题格式_高中化学:选择题答题方法与知识点总结,让你轻松秒杀各种难题...

选择题是化学考试中被广泛采用的一种题型。它具有知识容量大&#xff0c;覆盖面广&#xff0c;构思新颖、灵活巧妙&#xff0c;考试的客观性强&#xff0c;答题简单&#xff0c;评分容易、客观准确等优点。 选择题按考查形式可分为三种类型&#xff0c;即&#xff1a;常规型选择…

C++ STL详解(2)

点击蓝字关注我们来源自网络&#xff0c;侵删刷题时常用的STLstring之前写过一篇 string 的简介但是不是特别全面&#xff0c;这里再补充说明一下。size()返回字符串中字符的数量#include<iostream> #include<string>using namespace std;int main() {string str …

【lua学习】5.栈和lua_State

1 背景2 栈&#xff08;寄存器数组&#xff09;&#xff0c;虚拟机&#xff0c;全局状态机2.1 栈定义在lua_State结构体中2.2 global_State 全局状态机2.3 lua_newstate 主虚拟机和全局状态机的创建2.4 lua_close 关闭虚拟机3 栈上的地址3.1 假索引3.2 根据数字索引获取栈上的地…

python中反向切片用法_使用Python中的切片[:0:-1]反转列表

举个例子a [1, 2, 3, 4, 4, 5, 6, 9]如果你试着用正指数分割它newa a[1:5]这将导致newa [2, 3, 4, 4]这是因为&#xff0c;在上面这种情况下&#xff0c;切片是这样发生的&#xff0c;[包含&#xff1a;排他的]&#xff0c;包括第一个索引&#xff0c;切片从这个索引开始&am…

斐波那契数列的四种实现方式(C语言)

点击蓝字关注我们来源自网络&#xff0c;侵删斐波那契数列是一组第一位和第二位为1&#xff0c;从第三位开始&#xff0c;后一位是前两位和的一组递增数列&#xff0c;像这样的&#xff1a;0、1、1、2、3、5、8、13、21、34、55......今天&#xff0c;我们用四种方式来进行实现…

【lua学习】6.函数,闭包,错误处理

1 数据结构和宏1.1 Closure 闭包1.2 Proto 函数原型1.3 UpVal 外部局部变量(upvalue)1.4 LocVar 局部变量信息1.5 SParser 语法分析所需要的结构1.6 Zio 读写流对象1.7 Mbuffer 缓冲对象1.8 lua_Debug 调试信息1.9 CallInfo 函数调用信息1.10 lua_longjmp 跳转信息1.11 虚拟机状…

cdi 2.7.5_看一下CDI 2.0 EDR1

cdi 2.7.5CDI是最近对Java EE最好的补充之一。 该观点在用户和集成商之间广泛共享。 当前版本的CDI 1.2于2014年4月发布。现在&#xff0c;在2015年中期&#xff0c;我们将面对CDI 2.0规范的早期草案。 CDI 2.0将在Java 8和更高版本上运行。 最终版本计划于2016年发布&#xf…

linux make命令_第一章 1.3Linux下安装Redis

1.3.2 Linux下安装Redis第一步: 去官网下载安装包 ,传送门第二步: 上传到Linux服务器,解压redis的安装包tar -zxvf redis-6.0.8.tar.gz这里我已经解压好了,并且移动到了redis目录下第三步: 安装基本环境yum -y insatll gcc-c这里注意一个问题,Centos下安装的gcc默认版本为4.8.5…

C语言strcpy函数的使用

点击蓝字关注我们strcpy简单使用&#xff1a; #include <stdio.h> #include <string.h>struct Student {int sid;char name[20];int age;} st; //此处分号不可省略int main(void) {struct Student st {1000,"zhangsan",20};printf("%d %s %d\n&…

jooq_使用jOOQ DSL

jooq本文是我们学院课程“ jOOQ –类型安全数据库查询”的一部分 。 在SQL和特定关系数据库很重要的Java应用程序中&#xff0c;jOOQ是一个不错的选择。 当JPA / Hibernate抽象过多而JDBC过于抽象时&#xff0c;这是一种替代方法。 它显示了一种现代的领域特定语言如何可以极大…

什么镜头最适合拍风景_为什么您的风景摄影套件中应始终装有远摄镜头

当您考虑风景摄影镜头时&#xff0c;许多摄影师只考虑广角镜头。14-24mm f / 2.8或24-70mm f / 2.8等经典镜头是用于风景的流行光学元件。它们是绝佳的选择但是&#xff0c;也值得在风景摄影套件中使用长焦镜头。为什么要使用长镜头进行风景摄影&#xff1f;您在网上或浏览Inst…

C++ STL详解(3)

点击蓝字关注我们简介set 是 关联容器 的一种&#xff0c;是排序好的集合&#xff08;元素已经进行了排序&#xff09;。set 和 multiset 类似&#xff0c;它和 multiset 的差别在于 set 中不能有重复的元素。multiset 的成员函数 set 中也都有。使用 set 必须包含 #include<…

【lua学习】8.协程

1 数据结构和宏1.1 协程的状态码1.2 协程的执行状态码1.3 lua_State 协程结构体2 C API2.1 lua_newthread 新建一个协程&#xff0c;压栈&#xff0c;返回这个新协程2.2 luaE_freethread 释放一个协程L12.3 lua_status 获取协程的错误码2.4 lua_resume 唤醒一个协程&#xff0c…

检测到基于堆栈的缓冲区溢出_检测到堆栈粉碎

检测到基于堆栈的缓冲区溢出我敢打赌&#xff0c;每个Java开发人员在他们的职业生涯开始时第一次遇到Java代码的本机方法时都会感到惊讶。 我还可以肯定&#xff0c;多年来随着了解JVM如何通过JNI处理对本机实现的调用而使惊奇消失了。 这篇文章是关于本机方法的最新经验。 更…

探讨 C++ 虚函数 virtual

点击蓝字关注我们来源于网络&#xff0c;侵删有无虚函数的对比C 中的虚函数用于解决动态多态问题&#xff0c;虚函数的作用是允许在派生类中重新定义与积累同名的函数&#xff0c;并且可以通过基类指针或引用来访问基类和派生类中的同名函数。首先写两个简单的类&#xff0c;类…