[C++][异常]详解

目录

  • 1.C语言传统的处理错误的方式
  • 2.C++异常概念
  • 3.异常的抛出和捕获
  • 4.异常的重新抛出
  • 5.异常安全
  • 6.异常规范
  • 7.自定义异常体系
  • 8.C++标准库的异常体系
  • 9.异常的优缺点
    • 1.C++异常的优点
    • 2.C++异常的缺点
    • 3.总结

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

  • 传统的错误处理机制
    • 终止程序,如assert
      • 缺陷:用户难以接受。如发生内存错误,除0错误时就会终止程序
    • 返回错误码
      • 缺陷:需要程序员自己去查找对应的错误。如系统的很多库的接口函数都是通过把错误码放到errno中,表示错误
  • 实际中C语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的错误

2.C++异常概念

  • 异常是一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者处理这个错误
    • throw:当问题出现时,程序会抛出一个异常 --> 通过使用 throw关键字来完成
    • catch:在想要处理问题的地方,通过异常处理程序捕获异常catch关键字用于捕获异常,可以有多个catch进行捕获
    • try:*try块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个catch块
  • 如果有一个块抛出一个异常,捕获异常的方法会使用 trycatch 关键字。try块中放置可能抛出异常的代码,try块中的代码被称为保护代码
try
{// 保护的标识代码
}
catch (ExceptionName e1)
{// catch 块
}
catch (ExceptionName e2)
{// catch 块
}
catch (ExceptionName eN)
{// catch 块
}

3.异常的抛出和捕获

  • 异常的抛出和匹配原则
    • 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码
    • 选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个
    • 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象, 所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似于函数的传值返回)
    • **catch(…)可以捕获任意类型的异常,防止出现未捕获异常时,程序终止,**问题是不知道异常错误是什么
    • 实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配
      • **可以抛出的派生类对象,使用基类捕获,**在实际中非常实用
  • 在函数调用链中异常栈展开匹配原则
    • 首先检查throw本身是否在try块内部,如果是再查找匹配的catch语句。如果有匹配的,则跳到catch的地方进行处理
    • 没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch
    • 如果到达main函数的栈,依旧没有匹配的,则终止程序
      • 上述这个沿着调用链查找匹配的catch子句的过程称为栈展开
      • 所以实际中我们最后都要加一个catch(…)捕获任意类型的异常,否则当有异常没捕获,程序就会直接终止
    • 找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行
  • 抛异常可以抛任意类型对象
  • 捕获时,要求类型匹配
    请添加图片描述
double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);
}void Func()
{int len, time;cin >> len >> time;cout << Division(len, time) << endl;
}int main()
{try{Func();}catch (const char* errmsg) {cout << errmsg << endl;}catch(...){cout << "unkown exception" << endl;}return 0;}

4.异常的重新抛出

  • 有可能单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用链函数来处理,catch则可以通过重新抛出将异常传递给更上层的函数进行处理
double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}void Func()
{// 这里可以看到如果发生除0错误抛出异常,另外下面的array没有得到释放。// 所以这里捕获异常后并不处理异常,异常还是交给外面处理,这里捕获了再// 重新抛出去。int *array = new int[10];try{int len, time;cin >> len >> time;cout << Division(len, time) << endl;}catch (...){cout << "delete []" << array << endl;delete[] array;throw;  // 捕获什么抛出什么}// ...cout << "delete []" << array << endl;delete[] array;
}int main()
{try{Func();}catch (const char *errmsg){cout << errmsg << endl;}return 0;
}

5.异常安全

  • 构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化
  • 析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内存泄漏、句柄未关闭等)
  • C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII来解决以上问题

6.异常规范

  • 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的后面接throw(类型),列出这个函数可能抛掷的所有异常类型
  • 函数的后面接throw(),表示函数不抛异常
  • 若无异常接口声明,则此函数可以抛掷任何类型的异常
// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);// 这里表示这个函数只会抛出bad_alloc的异常
void *operator new(std::size_t size) throw(std::bad_alloc);// 这里表示这个函数不会抛出异常
void *operator delete(std::size_t size, void *ptr) throw();// C++11 中新增的noexcept,表示不会抛异常
thread() noexcept;
thread(thread &&x) noexcept;

7.自定义异常体系

  • 实际使用中很多公司都会自定义自己的异常体系进行规范的异常管理
  • 因为一个项目中如果大家随意抛异常,那么外层的调用者基本就没办法玩了
  • 所以实际中都会定义一套继承的规范体系。 这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了
    请添加图片描述
// 服务器开发中通常使用的异常继承体系
class Exception
{public:Exception(const string &errmsg, int id): _errmsg(errmsg), _id(id){}virtual string what() const{return _errmsg;}protected:string _errmsg;int _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;
};void SQLMgr()
{srand(time(0));if (rand() % 7 == 0){throw SqlException("权限不足", 100, "select * from name = '张三'");}// throw "xxxxxx";
}void CacheMgr()
{srand(time(0));if (rand() % 5 == 0){throw CacheException("权限不足", 100);}else if (rand() % 6 == 0){throw CacheException("数据不存在", 101);}SQLMgr();
}void HttpServer()
{// ...srand(time(0));if (rand() % 3 == 0){throw HttpServerException("请求资源不存在", 100, "get");}else if (rand() % 4 == 0){throw HttpServerException("权限不足", 101, "post");}CacheMgr();
}int main()
{while (1){try{HttpServer();}catch (const Exception& e) // 这里捕获父类对象就可以{// 多态cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}}return 0;
}

8.C++标准库的异常体系

  • C++提供了一系列标准的异常,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的

请添加图片描述
请添加图片描述

  • 说明:
    • 实际中我们可以去继承exception类实现自己的异常类
    • 但是实际中很多公司像上面一 样自己定义一套异常继承体系。因为C++标准库设计的不够好用

9.异常的优缺点

1.C++异常的优点

  • 异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助更好的定位程序的bug
  • 返回错误码的传统方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,那么得层层返回错误,最外层才能拿到错误
  • 很多的第三方库都包含异常,比如boost、gtest、gmock等等常用的库,那么我们使用它们也需要使用异常
  • 部分函数使用异常更好处理
    • 比如构造函数没有返回值,不方便使用错误码方式处理
    • 比如 T& operator[size_t pos]这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误

2.C++异常的缺点

  • 异常会导致程序的执行流乱跳,并且非常的混乱,并且是运行时出错抛异常就会乱跳。这会导致我们跟踪调试时以及分析程序时,比较困难
  • 异常会有一些性能的开销。当然在现代硬件速度很快的情况下,这个影响基本忽略不计
  • C++没有垃圾回收机制,资源需要自己管理。有了异常非常容易导致内存泄漏、死锁等异常安全问题。这个需要使用RAII来处理资源的管理问题。学习成本较高
  • C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常的混乱
  • 异常尽量规范使用,否则后果不堪设想,随意抛异常,外层捕获的用户苦不堪言。所以异常规范有两点:
    • 一、抛出异常类型都继承自一个基类
    • 二、函数是否抛异常、抛什么异常,都使用 func() throw();的方式规范化
    • 由于历史登各种原因,规范不是强制的

3.总结

  • 异常总体而言,利大于弊,所以工程中还是鼓励使用异常的
  • 另外OO的语言基本都是用异常处理错误,这也可以看出这是大势所趋

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

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

相关文章

Linux gcc day5粘滞位

粘滞位 背景&#xff1a;一定时在一个公共目录&#xff08;root创建&#xff09;下。进行临时文件的操作 Linux系统中有很多人&#xff0c;我们需要在一个公共目录下&#xff0c;进行临时文件的操作&#xff08;增删查改&#xff09; 创建一个根目录下的dir&#xff08;mytmp…

代码随想录算法训练营day33

题目&#xff1a;1005.K次取反后最大化的数组和、134. 加油站、135. 分发糖果 参考链接&#xff1a;代码随想录 1005.K次取反后最大化的数组和 思路&#xff1a;本题还是直觉&#xff0c;想使得整体的数组和最大&#xff0c;需要每一次取反都尽可能使的全局最优。先将数组排…

与汇智知了堂共舞,HW行动开启你的网络安全新篇章!

**网安圈内一年一度的HW行动来啦&#xff01; ** 招募对象 不限&#xff0c;有HW项目经验 或持有NISP二级、CISP证书优先 HW时间 以官方正式通知为准 工作地点&#xff1a;全国 薪资待遇 带薪HW &#xff08;根据考核成绩500-4000元/天不等&#xff09; 招募流程 1.填写报名…

供应链领域主题:生产制造关键术语和系统

BOM&#xff08;Bill of Material&#xff09;物料清单 BOM&#xff08;Bill of Material&#xff09;物料清单&#xff0c;是计算机可以识别的产品结构数据文件&#xff0c;也是ERP的主导文件。BOM使系统识别产品结构&#xff0c;也是联系与沟通企业各项业务的纽带。ERP系统中…

国内通稿在海外新闻媒体如何宣发-大舍传媒

引言 在全球化的时代背景下&#xff0c;海外通稿成为了新闻媒体间交流和宣发的重要方式之一。本文将探讨海外通稿在新闻媒体中的宣发方式&#xff0c;并分析大舍传媒在这方面的成功经验。 来百度APP畅享高清图片 海外通稿的重要性 海外通稿是指由海外记者或通讯社撰写的报道…

JavaScript - 你知道数组去重都有哪些实现方案吗

难度级别:初级及以上 提问概率:70% 数组去重是一道非常经典而又高频的面试题,这里我们提出6种解决方案: 目录 1 第一种 2 第二种 3 第三种 4 第四种

Codigger Desktop:用户体验与获得收益双赢的革新之作(一)

上周&#xff0c;我们介绍了Codigger Desktop凭借其强大的功能、稳定的性能以及人性化的设计&#xff0c;成为了广大开发者的得力助手。Codigger Desktop除了是开发者的利器外&#xff0c;它以其出色的用户体验和创新的收益模式&#xff0c;为用户提供了一个全新的选择。Codigg…

基于LNMP环境上线QQ农场

目录 一.介绍 二. 环境准备 三.安装Mysql数据库 四.安装PHP 五.安装Nginx 六.测试Nginx服务于PHP服务是否能关联 七.项目上线 QQ农场源码&#xff1a;做本项目默认操作者有一定的基础知识与理解能力 链接&#xff1a;https://pan.baidu.com/s/1HF8GZ-yvNh7RbJ61nXOW-g?…

【Django开发】0到1美多商城项目md教程第6篇:账号登录,1. 用户名登录逻辑分析【附代码文档】

美多商城完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;欢迎来到美多商城&#xff01;&#xff0c;项目准备。展示用户注册页面&#xff0c;创建用户模块子应用。用户注册业务实现&#xff0c;用户注册前端逻辑。图形验证码&#xff0c;图形验证码接口设…

【漏洞复现】泰博云平台 solr SSRF漏洞

0x01 产品简介 泰博云平台,就是指以电商集群的方式,通过供应链有效连接组成“商务云”生态系统,在产品、服务、营销推广等方面实现资源共享,“物”就是线下实体店网络,以众包模式,将行业制造商、分销商、零售商,和提供本土化设计、物流、安装的优质服务商,纳入统一的云…

Java 异常处理

什么是异常&#xff1f; 程序运行时&#xff0c;发生的不被期望的事件&#xff0c;它阻止了程序按照程序员的预期正常执行&#xff0c;这就是异常。异常发生时&#xff0c;是任程序自生自灭&#xff0c;立刻退出终止&#xff0c;还是输出错误给用户&#xff1f;或者用C语言风格…

lego-loam代码解析(2)-自用

学习文章&#xff1a; [1]LeGO-LOAM分析之建图&#xff08;三&#xff09; [2] LeGo-LOAM 源码解析-WinFrom控件库 [3] LeGO-LOAM批注版 [4]LeGO-LOAM 源码阅读笔记&#xff08;mapOptmization.cpp&#xff09; 整合拼起来的&#xff0c;自用 关于transformFusion.cpp 融合粗、…

程序汪10万接的多平台视频分发项目,模拟人工发视频

本项目来自程序汪背后的私活小团队&#xff0c;开发了一个多平台分发视频项目&#xff0c;给粉丝分享一下解决方案和具体项目分开情况付款情况等等细节&#xff0c;希望给想接私活的朋友一些经验参考 程序汪10万接的多平台视频分发项目&#xff0c;模拟人工发视频 视频版本 在 …

vs2017离线安装(配合QT5.9.2使用)

以vs2017_Professional版本为例&#xff1a; 一、下载安装包vs2017_Professional.exe&#xff08;在线安装包即可&#xff09; 二、创建在目录&#xff1a;C:\vs2017_Professional_Package&#xff0c;把vs2017_Professional.exe放在该目录下。 ID&#xff1a; Microsoft.Vis…

路径规划——曲线拟合详解(二):贝塞尔曲线、B样条曲线与QP优化( Fast-Planner算法核心部分)

1. 贝塞尔曲线 (1). 贝塞尔曲线的作用 贝塞尔曲线的作用是给定控制点&#xff0c;通过控制点生成对应的曲线进行轨迹拟合&#xff0c;输入为点&#xff0c;输出为受到控制点约束而产生的轨迹。 (2). 贝塞尔曲线的数学表达式 假设给定N个控制点&#xff0c;得到的为N-1阶的贝…

【Segment Anything Model】十三:Meta的最新工作EfficientSAM,微调到自己的数据集,代码。

&#x1f349; 博主微信 cvxiayixiao 还有其他专栏点击头像查询 &#x1f353; 【Segment Anything Model】计算机视觉检测分割任务专栏。 &#x1f351; 【公开数据集预处理】特别是医疗公开数据集的接受和预处理&#xff0c;提供代码讲解。 &#x1f348; 【opencv图像处理】…

【LeetCode热题100】118. 杨辉三角(动态规划)

一.题目要求 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 二.题目难度 简单 三.输入样例 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示…

Android Studio gradle-8.4 配置 GreenDao

1.配置项目下的build buildscript {repositories {mavenCentral()}dependencies {classpath ("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0")classpath ("org.greenrobot:greendao-gradle-plugin:3.3.1") // 使用最新版本} } 2.配置app下的build i…

跨越网络边界:借助C++编写的下载器程序,轻松获取Amazon商品信息

背景介绍 在数字化时代&#xff0c;数据是新的石油。企业和开发者都在寻找高效的方法来收集和分析网络上的信息。亚马逊&#xff0c;作为全球最大的电子商务平台之一&#xff0c;拥有丰富的商品信息&#xff0c;这对于市场分析和竞争情报来说是一个宝贵的资源。 问题陈述 然…

持续交付工具Argo CD的部署使用

Background CI/CD&#xff08;Continuous Integration/Continuous Deployment&#xff09;是一种软件开发流程&#xff0c;旨在通过自动化和持续集成的方式提高软件交付的效率和质量。它包括持续集成&#xff08;CI&#xff09;和持续部署&#xff08;CD&#xff09;两个主要阶…