六、C++语言初阶:异常

2023-10-26

6:异常

6.1 为什么需要异常?

  • 异常机制的处理原理
    程序会出现错误,尤其是不易察觉的错误。需要了解并解决这些错误。通常,程序出现错误,都会强制退出,很难排除错误原因。

6.2 C语言如何表示错误

1、函数返回值

  • 通常,成功返回0,返回值-1。
  • 返回值为指针类型,成功返回非NULL,失败返回值NULL。
    例如:malloc();例外shmat()失败返回值为MAP_INVALD(-1)
  • 其它另类的返回值
    fread()/fwrite()返回读写字符长度size_t,超出长度表示失败。

2、全局变量errno

6.3 异常处理特点

异常提供一个错误专用通道。

  • 优点:
    不干扰正常的返回值。
    必须处理异常。

6.4 语法

异常分为两个部分:抛出异常捕获并处理异常

  • 抛出异常
throw 表达式;
  • 捕获并处理异常
try {  
        // 保护代码 包含可能抛出异常的语句;  
} catch (类型名 [形参名]) {  
        // catch块 处理异常
}  
  • 特点
    1、只要抛出异常,异常后的代码不再执行。
    2、异常的所抛出与经过的栈都会销毁。
    3、异常捕获具有类型匹配,只有相同的或者父类类型才能匹配到。
    4、如果多个catch都能接受相同异常,只有最前面的一个可以接收到。catch(...)只能放在所有异常捕获的最后

多个catch表达式:

try {  
        // 保护代码 包含可能抛出异常的语句;  
} catch (类型名1 [形参名]) {  
        // catch块 处理异常
} catch (类型名2 [形参名]) {  
        // catch块 处理异常
} catch (类型名3 [形参名]) {  
        // catch块 处理异常
} catch(...){
        // catch块 处理异常
}

实例:多个catch

#include <iostream>
#include <cmath>
using namespace std;
//三角形
class Triangle{
protected: //允许子类访问,不允许对象访问
    int a,b,c;
public:
    Triangle():a(0),b(0),c(0){} //初始化成员变量
    Triangle(int a,int b,int c):a(a),b(b),c(c){
    	if(a <= 0 || b <= 0 || c <= 0)throw invalid_argument("三角形三边不能为0或负数");
	if(!Check()) throw invalid_argument("三边不能构成三角形");
    } //构造函数
    int GetLength() const {  //const修饰this指针
    	return a+b+c;
    }
    virtual float GetArea(){
    	float q = GetLength()/2.0;
	return sqrt(q*(q-a)*(q-b)*(q-c));
    }
    bool Check()const{
        return a+b > c && a+c > b && b+c > a;
    }
};
//直角三角形
class RightAngleTriangle:public virtual Triangle{
public:
    bool ChechRightAngleTriangle(){
    	return a*a+b*b == c*c;
    }
    RightAngleTriangle(int a,int b,int c):Triangle(a,b,c){
    	if(!ChechRightAngleTriangle()) throw invalid_argument("三边不能构成直角三角形");
    }
    float GetArea()const{
    	throw runtime_error("error");
	return a*b/2.0;
    }
};
int main(){
    int a,b,c;
    cin >> a >> b >> c;
    try{
        RightAngleTriangle rat(a,b,c);
	cout << rat.GetLength() << "," << rat.GetArea() << endl;
    }catch(invalid_argument& e){
    	cout << e.what() << endl;
    }catch(exception& e){
    	cout << "exception" << e.what() << endl;
    }catch(...){  //其
-1 2 3
三角形三边不能为0或负数
1 2 3
三边不能构成三角形
5 6 7
三边不能构成直角三角形

测试用例3,4,5求完周长之后,抛出异常,不再求面积

3 4 5
12,exception:error

注:异常机制try-throw-catch的目标是问题检测与问题解决分离

实例:实现除法

#include <iostream>
using namespace std;

int main() {
    int n,m;
    cin >> n >> m;
    //if(0 == m) throw "除数不能为0";
    try {
        if(0 == m) throw runtime_error("除数不能为0");
        cout << n/m << endl;
    } catch(exception& e) {
        cout << e.what() << endl;
    }
}
2 0
除数不能为0

6.5 异常的接口声明/异常规范

返回值类型 函数() throw(异常列表);

指定函数可以抛出何种异常,如果没有throw(异常列表)默认可以抛出所有异常。
指定函数不抛出函数,异常列表为空throw()

  • 那么当异常抛出后新对象如何释放?
    异常处理机制保证:异常抛出的新对象并非创建在函数栈上,而是创建在专用的异常栈上,因此它才可以跨接多个函数而传递到上层,否则在栈清空的过程中就会被销毁。所有从try到throw语句之间构造起来的对象的析构函数将被自动调用。但如果一直上溯到main函数后还没有找到匹配的catch块,那么系统调用terminate()终止整个程序,这种情况下不能保证所有局部对象会被正确地销毁。

  • 实例1:捕获异常

#include <iostream>
using namespace std;

void test(){
    cout << "before throw." << endl;
    throw -1;
    cout << "after throw." << endl;
}
int main(){
    try{
        test();
    }catch(int a){
        cout << "exception:" << a << endl;
    }
}
before throw.
exception:-1

实例2:异常与局部对象析构

#include <iostream>
using namespace std;

class Test{
public:
    Test(){
        cout << "Test Init" <<endl;
    }
    ~Test(){
        cout << "Test Destroy" <<endl;
    }
};

int main(){
    try{
        Test t;
        cout << "before throw." << endl;
        throw -1;
        cout << "after throw." << endl;
    }catch(int a){
        cout << "exception:" << a << endl;
    }
}
Test Init
before throw.
Test Destroy
exception:-1

注:
1、如果抛出的异常一直没有函数捕获(catch),则会一直上传到c++运行系统那里,导致整个程序的终止。
2、一般在异常抛出后资源可以正常被释放,但注意如果在类的构造函数中抛出异常,系统是不会调用它的析构函数的,处理方法是:如果在构造函数中要抛出异常,则在抛出前要记得删除申请的资源。
3、异常处理仅仅通过类型而不是通过值来(switch-case)匹配的,所以catch块的参数可以没有参数名称,只需要参数类型
4、函数原型中的异常说明要与实现中的异常说明一致,否则容易引起异常冲突。
5、应该在throw语句后写上异常对象时,throw先通过Copy构造函数构造一个新对象,再把该新对象传递给 catch.
6、catch块的参数推荐采用地址传递而不是值传递,不仅可以提高效率,还可以利用对象的多态性。另外,派生类的异常捕获要放到父类异常捕获的前面,否则,派生类的异常无法被扑获。
7、编写异常说明时,要确保派生类成员函数的异常说明和基类成员函数的异常说明一致,即派生类改写的虚函数的异常说明至少要和对应的基类虚函数的异常说明相同,甚至更加严格,更特殊。

6.6 自定义异常类

  • 编码流程
    1.继承异常类exception
    2.实现接口what()
  • 代码结构
class 异常类:public exception {    
public:    
   const char* what()const throw() {
        return 信息字符串;    
   }        
}; 
  • 实例:
#include <iostream>
using namespace std;

// 自定异常类
class SimpleException:public exception{
    string msg;
public:
    SimpleException(const string& msg):msg(msg){}
    const char* what() const throw(){
           return msg.c_str();
    }
};

class Simple{
public:
    Simple(){
        cout << __func__ << endl;
        throw SimpleException("Simple::Simple() Error");//runtime_error("construction error");
    }
    ~Simple(){cout << __func__ << endl;}
    void Test(){
        throw runtime_error("error");
    }
};

int main(){
    try{
        Simple s;
        s.Test();
    }catch(exception& e){
        cout << e.what() << endl;
    }
}
Simple
Simple::Simple() Error
  • 构造函数、析构函数的异常处理
    1、构造函数可以抛出异常,此时不会调用析构函数,所以如果抛出异常前,申请了资源,需要自己释放。
    2、C++标准指明析构函数不能、也不应该抛出异常。
    3、C++标准规定,构造函数失败,析构函数不会执行。就是说在构造函数抛出异常前分配的资源将无法释放。
    4、如果析构函数抛出异常,则异常点之后的程序不会执行,如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源,则这些动作不会执行,会造成诸如资源泄漏的问题。
    4、通常异常发生时,c++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃的问题。

6.7 是否使用异常机制

为什么很多经典书籍鼓励使用异常,但是实际开发中很多C++编码规范却禁用异常?
C++异常机制在语法上是更加优雅的处理错误,但是实际上编译出来的程序会有一些性能损失,另外错误地使用异常处理代码会变得更加复杂。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

六、C++语言初阶:异常 的相关文章

  • WPF DataGrid 多选

    我读过几篇关于这个主题的文章 但很多都是来自 VS 或框架的早期版本 我想做的是从 dataGrid 中选择多行并将这些行返回到绑定的可观察集合中 我尝试创建一个属性 类型 并将其添加到可观察集合中 它适用于单个记录 但代码永远不会触发多个
  • BASIC 中的 C 语言中的 PeekInt、PokeInt、Peek、Poke 等效项

    我想知道该命令的等效项是什么Peek and Poke 基本和其他变体 用 C 语言 类似PeekInt PokeInt 整数 涉及内存条的东西 我知道在 C 语言中有很多方法可以做到这一点 我正在尝试将基本程序移植到 C 语言 这只是使用
  • 没有特殊字符的密码验证器

    我是 RegEx 的新手 已经进行了大量搜索 但没有找到任何具体内容 我正在编写一个验证密码字符串的正则表达式 可接受的字符串必须至少具有 4 种字符类型中的 3 种 数字 小写字母 大写字母 特殊字符 我对包含有一个想法 也就是说 如果这
  • 通过引用传递 [C++]、[Qt]

    我写了这样的东西 class Storage public Storage QString key const int value const void add item QString int private QMap
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • 如何从本机 C(++) DLL 调用 .NET (C#) 代码?

    我有一个 C app exe 和一个 C my dll my dll NET 项目链接到本机 C DLL mynat dll 外部 C DLL 接口 并且从 C 调用 C DLL 可以正常工作 通过使用 DllImport mynat dl
  • 如何在 C++ 中标记字符串?

    Java有一个方便的分割方法 String str The quick brown fox String results str split 在 C 中是否有一种简单的方法可以做到这一点 The 增强分词器 http www boost o
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • WPF 数据绑定到复合类模式?

    我是第一次尝试 WPF 并且正在努力解决如何将控件绑定到使用其他对象的组合构建的类 例如 如果我有一个由两个单独的类组成的类 Comp 为了清楚起见 请注意省略的各种元素 class One int first int second cla
  • ASP.NET Core 3.1登录后如何获取用户信息

    我试图在登录 ASP NET Core 3 1 后获取用户信息 如姓名 电子邮件 id 等信息 这是我在登录操作中的代码 var claims new List
  • C# 列表通用扩展方法与非通用扩展方法

    这是一个简单的问题 我希望 集合类中有通用和非通用方法 例如List
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • C++ 中的 include 和 using 命名空间

    用于使用cout 我需要指定两者 include
  • 现代编译器是否优化乘以 1 和 -1

    如果我写 template
  • 从 mvc 控制器使用 Web api 控制器操作

    我有两个控制器 一个mvc控制器和一个api控制器 它们都在同一个项目中 HomeController Controller DataController ApiController 如果我想从 HomeController 中使用 Dat

随机推荐

  • 编写QT程序时发现内存泄漏的解决方法

    最近项目结尾进行测试的时候 发现项目持续运行产生大量数据后内存的消耗会无休止的增加 当关闭该窗口时 内存却并没有如期释放 理论上QT中所有的子对象在窗口被销毁时都会一同销毁 最终发现是我在这篇博客上写的添加长按的效果导致的 https bl
  • ipynb 格式文件

    最近碰到文件名后缀为 ipynb文件 起初没太在意这种文件格式 用Notepad 打开之后看到也是类似于JSON格式的信息 以为也是为其他的一些文件服务的 类似于配置一些HTML文件的配置文件 但是后来才发现这也是一种文本表示形式 只不过需
  • Lua:调试篇

    1 Lua代码编辑工具 辣博推荐 ZeroBrane Studio编写Lua脚本还是不错滴 基本的代码补全和提示都具有 按照从下往上的代码逻辑 还可以自动对齐格式 实话讲 还不是很完美 毕竟 作为一个使用习惯Qt如此完美的IDE工具的 Qt
  • [线性dp] aw897. 最长公共子序列(重要模板题+最长公共子序列模型)

    文章目录 0 前言 1 LCS 模板题 0 前言 LCS longest common sub sequences 最长公共子序列 子串 按原顺序依次出现 禁止跳过某元素的序列 具有连续性 子序列 在保持元素前后关系的前提下 可以跳过某些元
  • C语言文件指针设置偏移量--fseek

    一 fseek fseek是设置文件指针偏移量的函数 具体传参格式为 int fseek FILE stream long int offset int whence 返回一个整数 其中 1 stream是指向文件的指针 2 offset是
  • (亲测可用)html5 file调用手机摄像头

    在切图网一个客户的webapp项目中需要用到 html5调用手机摄像头 找了很多资料 大都是 js调用api 然后怎样怎样 做了几个demo测试发现根本不行 后来恍然大悟 用html5自带的 input file 纯html5 并且不涉及到
  • composer.json和composer.lock到底是什么以及区别?

    composer方文档 https docs phpcomposer com 04 schema html我们在做项目的时候 总是要安装一些依赖 composer给我们提供了很多方便 直接运行composer install 当我们运行co
  • Keras 深度学习之猫狗大战

    项目地址和代码 Project Dogs vs Cats 项目详细报告 Report dogs vs cats pdf keras 版本 2 1 5使用滴滴云AI大师码 0212 消费GPU有9折优惠哦 1 问题定义和数据集获取 项目属于计
  • Android RecyclerView 实现瀑布流

    Android RecyclerView 使用大全 基础使用 item 动画 下拉刷新等 瀑布流也是个常用的显示控件了 但是在使用时经常遇到一些问题 比如滑动回顶部后出现空隙 item在滑动时乱跳等问题 下面就来说说我怎么实现的瀑布流 并且
  • 51单片机_动态数码管(定时器中断实现)

    动态数码管 定时器中断 原理 利用定时器0 定时5ms 中断溢出200次即为1s 1s数码管显示的数字加1 一直加到250后归0 全部代码 include
  • ASP.NET Web Pages基础知识---使用Chart 帮助器

    Chart 帮助器 可以创建不同类型的带有多种格式化选项和标签的图表图像 它可以创建面积图 条形图 柱形图 折线图 饼图等标准图表 也可以创建像股票图表这样的更专业的图表 在图表中显示的数据可以是来自一个数组 一个数据库 或者一个文件中的数
  • win10系统anaconda搭建tensorflow环境运行Mask_RCNN以及ubuntu中conda配置

    0 相关说明及anaconda下载安装 0 1 anaconda版本3 4 3 0 0 2 遇到的问题 PermissionError Errno 13 Permission denied C ProgramData Anaconda3 t
  • LeetCode详细题解-Java版

    个人在leetcode刷题的过程中 也记录了一些解题的过程 不一定是最优的 但是都能正确通过 还有一些是官方给的解答 本文会陆陆续续更新 有一些本人看到的一些好的解题博文 本文直接引用了原文 如涉及侵权或博文失效 请联系博主删除博文链接 L
  • elasticsearch的实现全文检索

    elasticsearch一个准实时的搜索引擎 基于lucene构建 它的主要强项还是在全文检索方面 工作中还是使用到了这部分功能 这里做一个简单的总结 可以使初次使用的人很快的配置和使用 一 全文检索的概念 首先介绍全文检索的概念 就是对
  • HBuilder X 连接苹果手机(IOS)详细教程。Windows: 连接iOS手机调试项目

    手机 苹果11 IOS版本 15 0 1 HBuilder X要是最新版本 2 需要下载iTunes 注意事项 Windows 32位 itunes下载地址 Windows 64位 itunes下载地址 建议从如上地址下载iTunes 如果
  • IDEA git 出现分支游离问题 You are in 'detached HEAD' state,

    Can t update no current branch You are in detached HEAD state which means that you re not on any branch Checkout a branc
  • 高速电路信号完整性分析常用名词

    时域特性与频域特性 从字面理解时域就是时间区域或者说时间范围 频域就是频率区域或者说频率范围 某个信号量随时间变化的特征 就是这个信号量的时域特性 信号的时域特性可以用时间波形显示 时域函数可以转换为频域函数 频域特性则是时域的积分变换 信
  • Postgre 时间间隔类型(Interval)

    业务场景 对时间进行操作 比如获取5分钟之前的时间 是 now 5 minute 但是有时后面的间隔值是动态的 需要动态拼装 这是就需要把拼装好的字符串转变为时间间隔类型interval 如下 SELECT now CASE WHEN co
  • windows 下 openGLES 3.0 配合 vs 环境搭建(一)

    1 OpenGL ES3 0 Programming guide 里的例子代码 下载地址 https codeload github com danginsburg opengles3 book zip master 2 下载mail op
  • 六、C++语言初阶:异常

    6 异常 6 1 为什么需要异常 异常机制的处理原理 程序会出现错误 尤其是不易察觉的错误 需要了解并解决这些错误 通常 程序出现错误 都会强制退出 很难排除错误原因 6 2 C语言如何表示错误 1 函数返回值 通常 成功返回0 返回值 1