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,一经查实,立即删除!

相关文章

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

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

C++ STL详解(1)

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

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

选择题是化学考试中被广泛采用的一种题型。它具有知识容量大&#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 …

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

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

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&…

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

当您考虑风景摄影镜头时&#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<…

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

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

探讨 C++ 虚函数 virtual

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

C++ STL详解(4)

点击蓝字关注我们来源于网络&#xff0c;侵删unordered_set / unordered_multiset这两个容器的方法与上一篇 C STL简介&#xff08;3&#xff09; 提到的 set / multiset 基本一样。但是要注意的是&#xff1a;这两个是无序的&#xff0c;基于哈希表实现的&#xff0c;增删改查…

js压缩图片_Web 性能优化: 图片优化让网站大小减少 62%

图像是web上提供的最基本的内容类型之一。他们说一张图片胜过千言万语。但是如果你不小心的话&#xff0c;图片大小有时高达几十兆。因此&#xff0c;虽然网络图像需要清晰明快&#xff0c;但它们尺寸可以缩小压缩的&#xff0c;使用加载时间保持在可接受的水平。在我的网站上&…

春春幼儿园堆积木大赛_春云边车

春春幼儿园堆积木大赛我有一个部署到基于NetflixOSS的云的应用程序&#xff0c;该应用程序具有以下结构&#xff1a; 本质上是一种将信息持久保存到Cassandra集群的服务。 所有应用程序都已注册到Eureka –因此&#xff0c;在本例中&#xff0c;该服务以及Cassandra节点都已在…

C++ 标准输入的行加速

点击蓝字关注我们来源于网络&#xff0c;侵删一、前言c/c 的标准输入函数&#xff0c;在输入一行数据&#xff0c;当数据量比较大的时候&#xff0c; cin 的效率是非常低的&#xff0c;为了加快读入的效率&#xff0c;可以改用 scanf&#xff0c;当然&#xff0c;还有一种利用 …

redis安装_Redis安装

redis安装本文是我们学院课程的一部分&#xff0c;标题为Redis NoSQL键值存储 。 这是Redis的速成课程。 您将学习如何安装Redis和启动服务器。 此外&#xff0c;您还会在Redis命令行上乱七八糟。 接下来是更高级的主题&#xff0c;例如复制&#xff0c;分片和集群&#xff0c…

C++ 自动锁

点击蓝字关注我们来源于网络&#xff0c;侵删一、锁1.锁 是 多线程编程 中一个很常用的概念&#xff0c;这里不多加介绍其原理&#xff0c;有兴趣可以参考 临界区 进行更多的了解&#xff1b;2.锁 一般会提供三个接口&#xff1a;加锁&#xff08;Lock&#xff09;、解锁&#…

C++ 浮点数精度判定

点击蓝字关注我们来源于网络&#xff0c;侵删一、引例看下下面这段代码&#xff0c;会输出什么结果呢&#xff1f;double x 0;for (int i 0; i < 10; i) {x 0.1;}printf("%d\n", x 1);输出如下&#xff1a;0引起这种反差的原因就是浮点误差&#xff0c;浮点数…

字节流和字符流哪个不刷新_不喜欢节流吗?

字节流和字符流哪个不刷新您别无选择–底层系统&#xff08;此处的JVM将为您完成选择&#xff09;。 我仍然记得2013年夏天&#xff0c;当时我正在运行一个项目&#xff0c;整个应用程序中只有1个URL使服务器瘫痪。 问题很简单-机器人决定以很高的速率索引我们的网站&#xff…

C/C++动态内存管理—(new与malloc)

点击蓝字关注我们来源于网络&#xff0c;侵删1.C/C内存分布虚拟地址空间分布&#xff1a;由C/C编译的程序占用的内存分为以下几个部分&#xff1a;栈区&#xff08;stack&#xff09;— 由编译器自动分配释放 &#xff0c;存放为运行函数而分配的局部变量、函数参数、返回数据、…