07-C++ 异常

异常

1. 概念

  • 异常事件(如:除 0 溢出,数组下标越界,所要读取的文件不存在,空指针,内存不足等等)

  • 在C 语言对错误的处理是两种方法:

    • 一是使用整型的 返回值标识错误
    • 二是使用 errno 宏(可以简单的理解为一个全局整型变量)去记录错误。
  • c++异常 不可忽略 (如果忽略,进程结束)。

    • 异常作为一个类,可以拥有自己的成员,这些成员就可以传递足够的信息。
    • 抛出异常 ----> 捕获异常

示例:

int main(int argc, char *argv[])
{ int num = 10 / 0;cout << "OVER" << endl;return 0;
} 
//不会显示OVER,程序异常结束

2. 抛出异常

语法:

throw 值或变量;  

例如:

throw 0;throw 1.1;throw 'a';throw "abc";

3. 捕获异常

语法:

try{可能会产生异常的代码111222 出现异常333
} 
catch(数据类型1 变量名)
{当throw的值与数据类型1相同进入此处
} 
catch(数据类型2 变量名)
{当throw的值与数据类型2相同进入此处
} 
...
catch(...)
{当throw的值以上数据类型都不相同进入此处
}

4. 示例

#include <iostream>using namespace std;class A{};
void my_error(int a, int b)
{if(b == 0){//抛出异常//throw 0;//throw 1.2;//throw 'a';//throw "abc";throw new A();}cout << a / b << endl;
}
void fun01()
{try{my_error(10, 0);}catch(int e){cout << "int抛出的异常值为:" << e << endl;}catch(double e){cout << "double抛出的异常值为:" << e << endl;}catch(char e){cout << "char抛出的异常值为:" << e << endl;}catch(...){cout << "除以上类型外所有异常" << endl;}
}int main(int argc, char *argv[])
{fun01();cout << "Hello World!" << endl;return 0;
}
//除以上类型外所有异常
//Hello World!

5. 栈解旋

概念:

  • 异常被抛出后,从进入 try 块起,到异常被抛掷前,这期间 在栈上构造的所有对象,都会被自动析构。

  • 析构的顺序与构造的顺序相反,这一过程称为栈的解旋 。

问题:在创建对象的过程中,抛出异常,此时 还 没来的及释放对象,所以会出现错误。

解决办法:用try…catch 捕获异常,就会自动释放内存空间

示例1:没有释放没存

#include <iostream>using namespace std;
class B{
private:int x;
public:B(int x){this->x = x;cout << "B." << x << "被创建了" << endl;}~B(){cout << "B." << x << "被销毁了" << endl;}
};
void fun02()
{B b1(1);B b2(2);B b3(3);//抛出异常throw 0;
}int main(int argc, char *argv[])
{fun02();cout << "Hello World!" << endl;return 0;
}

在这里插入图片描述

示例2:使用try…catch后,自动释放内存

#include <iostream>using namespace std;
class B{
private:int x;
public:B(int x){this->x = x;cout << "B." << x << "被创建了" << endl;}~B(){cout << "B." << x << "被销毁了" << endl;}};
void fun02()
{B b1(1);B b2(2);B b3(3);//抛出异常throw 0;
}int main(int argc, char *argv[])
{try{fun02();}catch(int e){}cout << "Hello World!" << endl;return 0;
}

在这里插入图片描述

6. 异常的接口声明

作用:限定异常抛出的类型

语法:

返回值类型 函数名(形参列表)throw(数据类型1,数据类型2,...)
{函数体
} 

注意:

  • 声明异常后,当前函数中只能抛出指定类型的异常
  • throw() 括号中啥也不写,表示不允许抛出任何异常

示例:

#include <iostream>using namespace std;class A{};void my_error(int a, int b)
{if(b == 0){//抛出异常//throw 0;//throw 1.2;//throw 'a';//throw "abc";throw new A();}cout << a / b << endl;
}
void fun01()
{try{my_error(10, 0);}catch(int e){cout << "int抛出的异常值为:" << e << endl;}catch(double e){cout << "double抛出的异常值为:" << e << endl;}catch(char e){cout << "char抛出的异常值为:" << e << endl;}catch(...){cout << "除以上类型外所有异常" << endl;}
}class B{
private:int x;
public:B(int x){this->x = x;cout << "B." << x << "被创建了" << endl;}~B(){cout << "B." << x << "被销毁了" << endl;}};
void fun02()
{B b1(1);B b2(2);B b3(3);//抛出异常throw 0;
}
//当前函数只能抛出int或char类型的异常
//void fun03() throw(int,char)
//throw() 说明当前函数不会抛出任何异常
void fun03() throw()
{throw 0;
}
int main(int argc, char *argv[])
{
//    try
//    {
//        fun03();
//    }
//    catch(int e)
//    {
//    }fun03();cout << "Hello World!" << endl;return 0;
}
// 此时会报错
//terminate called after throwing an instance of 'int'
//抛出'int'实例后调用终止

7. 异常对象的生命周期

  • 抛出异常对象
    • 多次 调用对象的构造和析构
  • 抛出异常对象指针
    • 只调用 构造函数,没有析构
  • 抛出异常对象引用 (推荐使用)
    • 只会调用一次构造,一次析构
    • 注意:隐式创建对象,不然会触发拷贝构造

7.1 示例1:抛出异常对象

#include <iostream>using namespace std;
class MyError
{
public:MyError(){cout << "构造函数" << endl;}MyError(const MyError& e){cout << "拷贝构造" << endl;}~MyError(){cout << "析构函数" << endl;}
};void test01()throw(MyError)
{throw MyError(); //调用构造 
}
void fun01()
{try{test01();}catch(MyError e) //调用拷贝构造{}
}int main(int argc, char *argv[])
{fun01();return 0;
}
//构造函数
//拷贝构造
//析构函数
//析构函数

注意:显示创建对象 会调用 构造和拷贝构造

7.2 示例2:抛出异常对象指针

#include <iostream>using namespace std;
class MyError
{
public:MyError(){cout << "构造函数" << endl;}MyError(const MyError& e){cout << "拷贝构造" << endl;}~MyError(){cout << "析构函数" << endl;}
};void test02()
{//new 返回的是error对象的指针throw new MyError();
}void fun02()
{try{test02();}catch(MyError *e){}
}int main(int argc, char *argv[])
{fun02();return 0;
}
//构造函数

7.3 示例3:抛出异常对象引用 (推荐使用)

#include <iostream>using namespace std;
class MyError
{
public:MyError(){cout << "构造函数" << endl;}MyError(const MyError& e){cout << "拷贝构造" << endl;}~MyError(){cout << "析构函数" << endl;}
};void test03()
{throw MyError();
}void fun03()
{try{test03();}catch(MyError& e){}
}
int main(int argc, char *argv[])
{fun03();return 0;
}
//构造函数
//析构函数

8. 异常的多态

概念:子类异常对象 可以被 父类异常类型捕获 ,原理是上行,子传父,多态。

示例1:

#include <iostream>using namespace std;class BaseException{};
class MyException01:public BaseException{};
class MyException02:public BaseException{};
void test05()
{try{throw MyException01();}catch(BaseException){cout << "可以捕获子类异常" << endl;}
}
int main(int argc, char *argv[])
{test05();return 0;
}
//可以捕获子类异常

示例2:重写父类虚函数

#include <iostream>using namespace std;
class BaseException{
public:virtual void printMsg(){}
};
class NullException:public BaseException{
public:void printMsg(){cout << "空指针异常" << endl;}
};
class ArrOutException:public BaseException{
public:void printMsg(){cout << "数组下标越界异常" << endl;}
};
void test05()
{try{throw NullException();}catch(BaseException &e){e.printMsg();}
}
int main(int argc, char *argv[])
{test05();return 0;
}
//空指针异常

9. 标准异常库

9.1 简介

标准库中也提供了很多的 异常类,它们是通过类 继承组织起来 的。

异常类继承层级结构图所示 :

在这里插入图片描述

标准异常类的成员:

① 在上述继承体系中,每个类都有提供了构造函数、复制构造函数、和赋值操作符重载。

② logicerror 类及其子类、 runtimeerror 类及其子类,它们的构造函数是接受一个string 类型的形式参数,用于异常信息的描述

③ 所有的异常类都有一个 what()方法,返回 const char* 类型(C 风格字符串)的值,描述异常信息。

标准异常类的具体描述:

异常名称描述
exception所有标准异常类的父类
bad_alloc当 operator new and operator new[],请求分配内存失败时
bad_exception这是个特殊的异常,如果函数的异常抛出列表里声明了 badexception 异常,当函数内部抛出了异常抛出列表中没有的异 常,这是调用的 unexpected 函数中若抛出异常,不论什么类 型,都会被替换为 badexception 类型
bad_typeid使用 typeid 操作符,操作一个 NULL 指针,而该指针是带有虚函数的类,这时抛出 bad_typeid 异常
bad_cast使用 dynamic_cast 转换引用失败的时候
ios_base::failureio 操作过程出现错误
logic_error逻辑错误,可以在运行前检测的错误
runtime_error运行时错误,仅在运行时才可以检测的错误

logic_error 的子类:

异常名称描述
length_error试图生成一个超出该类型最大长度的对象时,例如 vector 的 resize 操作
domain_error参数的值域错误,主要用在数学函数中。例如使用一个负值调 用只能操作非负数的函数
outofrange超出有效范围
invalid_argument参数不合适。在标准库中,当利用 string 对象构造 bitset 时, 而 string 中的字符不是’0’或’1’的时候,抛出该异常

runtime_error 的子类:

异常名称描述
range_error计算结果超出了有意义的值域范围
overflow_error算术计算上溢
underflow_error算术计算下溢
invalid_argument参数不合适。在标准库中,当利用 string 对象构造 bitset 时, 而 string 中的字符不是’0’或’1’的时候,抛出该异常

9.2 标准异常使用

示例:

#include <iostream>using namespace std;
void test02()
{//throw runtime_error("使用系统提供的运行时异常类");throw logic_error("逻辑错误");
}void fun02()
{try{test02();}
//    catch(runtime_error &e)
//    {
//        const char * msg = e.what();
//        cout << msg << endl;
//    }catch(exception &e){const char * msg = e.what();cout << msg << endl;}
}int main(int argc, char *argv[])
{fun02();cout << "Hello World!" << endl;return 0;
}
//逻辑错误

10. 自定义异常

步骤:

  1. 定义一个类
  2. 继承与异常类
  3. 重写wait方法
方式1: 继承总异常类 exception 需要冲写what() 函数
方式2: 继承总异常类Exception下的某一个 异常类,此处是 runtime_error

示例:

#include <iostream>
using namespace std;
//方式1: 继承总异常类 exception 需要冲写what() 函数
class my_error:public exception{
private://记录异常信息char *msg;
public:my_error(char *msg){this->msg = msg;}//重写父类 what() 函数const char* what() const _GLIBCXX_USE_NOEXCEPT{return msg;}
};//方式2: 继承总异常类Exception下的某一个 异常类,此处是 runtime_error
class my_error02:public runtime_error{
public:my_error02(char *msg):runtime_error(msg){}
};
//调用方式1
void test()
{throw my_error("自定义异常");
}
//调用方式2
void test02()
{throw my_error02("自定义异常2");
}
void fun01()
{try{test02();}catch(exception &e){cout << e.what() << endl;}
}int main(int argc, char *argv[])
{fun01();return 0;
}
//自定义异常2

11. 练习总结

11.1 示例1

#include <iostream>
#include <cstdio>
using namespace std;void test03(int a,int b)
{if(b == 0){throw "除数不能为0";}cout << a / b << endl;
}void test04()
{FILE* f = fopen("xxx","r");if(f == NULL){throw "文件路径不正确";}
}void fun03()
{try{test03(10,0);}catch(char const* e){cout << e << endl;}
}
void fun04()
{try{test04();}catch(char const* e){cout << e << endl;}
}
int main(int argc, char *argv[])
{fun03();fun04();return 0;
}
//除数不能为0
//文件路径不正确

11.2 示例2:ArrayList

arraylisat.hpp

#include <cstring>
template<class X>
class ArrayList{X *data;int size;int count;
public:ArrayList();~ArrayList();void add(X& x);X& get(int index);int getSize();
};template<class X>
ArrayList<X>::ArrayList()
{data = new X[2];count = 2;size = 0;
}template<class X>
ArrayList<X>::~ArrayList()
{delete [] data;
}template<class X>
void ArrayList<X>::add(X& x)
{if(size == count){count *= 2;X* newData = new X[count];memcpy(newData,data,size*sizeof(X));delete [] data;data = newData;}data[size] = x;size++;
}template<class X>
X& ArrayList<X>::get(int index)
{//判断传入的参数是否合规,应该 >0,<size;//否则抛出异常 0if(index < 0 || index >= size){throw 0;}return data[index];
}template<class X>
int ArrayList<X>::getSize()
{return size;
}

main.cpp

#include <iostream>
#include "arraylist.hpp"using namespace std;class A{int num;
public:A(){}A(int num):num(num){}void print(){cout << num << endl;}
};
int main(int argc, char *argv[])
{ArrayList<A> list;A d01 = 1;A d02 = 2;A d03 = 3;A d04 = 4;A d05 = 5;list.add(d01);list.add(d02);list.add(d03);list.add(d04);list.add(d05);//捕获异常,此时ArrayList中只有5个数据,参数10>sizetry{A& a = list.get(10);a.print();}catch(int e){cout << "有bug" << endl;}return 0;
}
//有bug

11.3 示例3:读取文件

① myutils.h

#ifndef MYFILE_UTILS_H
#define MYFILE_UTILS_H//声明读取文件函数
extern char *get_file_text(char *filepath);
#endif // MYFILE_UTILS_H

② filepath_error.h

#ifndef FILEPATH_ERROR_H
#define FILEPATH_ERROR_H
#include <iostream>
//定义路径错误函数,继承runtime_error
#include <iostream>
using namespace std;
class filepath_error:public runtime_error
{
public:filepath_error();
};
#endif // FILEPATH_ERROR_H

③ filepath_error.cpp

#include "filepath_error.h"filepath_error::filepath_error():runtime_error("文件路径有误")
{}

④ myutils.cpp

#include <iostream>
#include <cstdio>
#include <cstring>
#include "myfileutils.h"
#include "filepath_error.h"//根据文件名读取文件中的内容
char *get_file_text(char *filepath)
{FILE *f = fopen(filepath, "r");if(f == NULL){//throw 0;throw filepath_error();}//计算文本长度fseek(f, 0, 2);int len = ftell(f);//创建存放读取的文件的数组char *c = new char[len];//将数组数据置零memset(c, 0, len);//游标恢复置开始fseek(f, 0, 0);//读取数据fread(c, len, 1, f);fclose(f);return c;}
//甲乙丙丁戊己庚辛壬癸
//子丑寅卯陈思武威申酉戌亥

⑤ main.cpp

#include <iostream>
#include "myfileutils.h"
#include "filepath_error.h"using namespace std;int main(int argc, char *argv[])
{try{//char *content = get_file_text("D:/io");char *content = get_file_text("D:/a.txt");cout << content << endl;}catch(exception &e){cout << e.what() << endl; //文件路径有误}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Spring系列学习四、Spring数据访问

Spring数据访问 一、Spring中的JDBC模板介绍1、新建SpringBoot应用2、引入依赖&#xff1a;3、配置数据库连接&#xff0c;注入dbcTemplate对象&#xff0c;执行查询&#xff1a;4&#xff0c;测试验证&#xff1a; 二、整合MyBatis Plus1&#xff0c;在你的项目中添加MyBatis …

基于uibot知网文论采集机器人设计与实现

摘要 人工智能技术的不断更新迭代为财务数据自动化处理带来了新的机遇和挑战&#xff0c;如何通过人工智能等新兴技术来优化现有的财务流程&#xff0c; 创造更多的企业价值&#xff0c;成为财务信息自动化处理是目前的重点研究方向。机器人流 程自动化作为一种新型的自动化技…

SpringBoot项目部署及多环境

1、多环境 2、项目部署上线 原始前端 / 后端项目宝塔Linux容器容器平台 3、前后端联调 4、项目扩展和规划 多环境 程序员鱼皮-参考文章 本地开发&#xff1a;localhost&#xff08;127.0.0.1&#xff09; 多环境&#xff1a;指同一套项目代码在把不同的阶段需要根据实际…

三个故事,谈谈小米汽车技术发布会

都说新年新气象&#xff0c;随着年末消费旺季到来&#xff0c;汽车市场越来越热闹了。 继蔚来12月23日公布旗舰车型ET9&#xff0c;华为26日发布问界M9&#xff0c;小米汽车首款量产车型SU7终于正式亮相。 12月28日&#xff0c;在小米汽车技术发布会上&#xff0c;小米创办人…

【STM32】STM32学习笔记-TIM输出比较(15)

00. 目录 文章目录 00. 目录01. 输出比较简介02. PWM简介03. 输出比较通道(高级)04. 输出比较通道(通用)05. 输出比较模式06. PWM基本结构07. PWM参数计算08. 舵机简介09. 舵机硬件电路10. 直流电机及驱动简介11. 直流电机硬件电路12. 附录 01. 输出比较简介 OC&#xff08;Ou…

paypal实操常见问题——绑卡篇

1、绑美金提款卡的时候卡号类型怎么选&#xff1f; PayPal在绑定美金提现卡的时候&#xff0c;页面里会出来两个选项&#xff0c;一个是“关联借记卡或信用卡”&#xff0c;一个是“关联银行账户” “关联借记卡或信用卡”这个选项是消费的时候用来付款的卡&#xff1b; “关…

如何使用ArcGIS Pro自动矢量化建筑

相信你在使用ArcGIS Pro的时候已经发现了一个问题&#xff0c;那就是ArcGIS Pro没有ArcScan&#xff0c;在ArcGIS Pro中&#xff0c;Esri确实已经移除了ArcScan&#xff0c;没有了ArcScan我们如何自动矢量化地图&#xff0c;从地图中提取建筑等要素呢&#xff0c;这里为大家介绍…

验证 Mixtral-8x7B-Instruct-v0.1 和 LangChain SQLDatabaseToolkit 的集成效果

验证 Mixtral-8x7B-Instruct-v0.1 和 LangChain SQLDatabaseToolkit 的集成效果 0. 背景1. 验证环境说明2. 验证开始2-1. 准备测试数据库2-2. 读取环境配置信息2-3. 导入依赖包2-3. 创建 SQLDatabaseToolkit 对象和 AgentExecutor 对象2-4. 第1个测试 - 描述一个表2-5. 第2个测…

【动态规划精选题目】3、简单多状态模型

此动态规划系列主要讲解大约10个系列【后续持续更新】 本篇讲解简单多状态模型中的9道经典题&#xff0c;会在讲解题目同时给出AC代码 目录 1、按摩师 2、力扣198:打家劫舍1 3、打家劫舍II 4、删除并获得点数 5、 粉刷房子 6、力扣309:买卖股票的最佳时机含冷冻期 7、 买…

Spring-4-代理

前面提到过&#xff0c;在Spring中有两种类型的代理&#xff1a;使用JDK Proxy类创建的JDK代理以及使用CGLIB Enhancer类创建的基于CGLIB的代理。 你可能想知道这两种代理之间有什么区别&#xff0c;以及为什么 Spring需要两种代理类型。 在本节中&#xff0c;将详细研究代理…

基于策略模式和简单工厂模式实现zip、tar、rar、7z四种压缩文件格式的解压

推荐语 这篇技术文章深入探讨了基于策略模式和简单工厂模式实现四种常见压缩文件格式的解压方法。通过阅读该文章&#xff0c;你将了解到如何利用这两种设计模式来实现灵活、可扩展的解压功能&#xff0c;同时适应不同的压缩文件格式。如果你对设计模式和文件处理感兴趣或刚好…

【JavaEE进阶】 @RequestMapping注解

文章目录 &#x1f384;什么是RequestMapping 注解&#x1f333;RequestMapping 使⽤&#x1f332;RequestMapping 是GET还是POST请求&#xff1f;&#x1f6a9;使用Postman构造POST请求 ⭕总结 &#x1f384;什么是RequestMapping 注解 在Spring MVC 中使⽤ RequestMapping 来…

基于Mapify的在线艺术地图设计

地图是传递空间信息的有效载体&#xff0c;更加美观、生动的地图产品也是我们追求目标。 那么&#xff0c;我们如何才能制出如下图所示这样一幅艺术性较高的地图呢&#xff1f;今天我们来一探究竟吧&#xff01; 按照惯例&#xff0c;现将网址给出&#xff1a; https://www.m…

PathWave Device Modeling (IC-CAP) 建模系统——IC-CAP概述

建模系统 PathWave Device Modeling&#xff08;IC-CAP&#xff09;建模系统用于测量半导体器件并分析器件的电路建模特性以及分析所得数据。 要使用PathWave Device Modeling&#xff08;IC-CAP&#xff09;&#xff0c;您需要以下设置&#xff1a; 一台工作站执行直流、电…

【rosbag】rosbag命令--查看提取bag中的内容

1.查看消息 rosbag info --.bag topics是其中的话题内容&#xff0c;/imu中只写了包含imu的数据&#xff0c;但是imu是9轴的&#xff0c;到底磁力计信息是否包含在其中呢&#xff1f; 2.将bag中的某个话题转为txt 将file_name.bag文件中topic_name话题的消息转换到Txt_name.…

java中的缓冲类HeapByteBuffer和DirectByteBuffer的区别

使用之前写的文章里的例子 https://blog.csdn.net/zlpzlpzyd/article/details/135292683 HeapByteBuffer import java.io.File; import java.io.FileInputStream; import java.io.Serializable; import java.nio.ByteBuffer; import java.nio.channels.FileChannel;public clas…

Linux升级指南:保持系统安全和高效运行

Linux系统的升级是确保系统稳定和安全性的重要步骤。本文将介绍Linux系统升级的基本概念&#xff0c;以及具体的操作步骤和注意事项&#xff0c;以帮助用户顺利升级他们的Linux系统。 Linux操作系统以其稳定性和可定制性而闻名&#xff0c;它经常通过升级来提供新的功能、修复漏…

【论文笔记】Radar Fields: An Extension of Radiance Fields to SAR

原文链接&#xff1a;https://arxiv.org/abs/2312.12961 1. 引言 本文针对合成孔径雷达&#xff08;SAR&#xff09;的3D重建&#xff0c;提出雷达场&#xff0c;基于多个SAR对场景的测量学习体积模型。 3. 辐射场的介绍 NeRF将静态场景表达为连续的体积函数 F \mathcal{F}…

服装店收银系统 助力完善线上线下方案

一个服装店收银系统可以助力完善线上线下方案&#xff0c;提供以下功能和优势&#xff1a; 1. 销售管理&#xff1a;收银系统可以记录每笔销售订单的详细信息&#xff0c;包括商品名称、价格、数量等&#xff0c;方便店主进行销售统计和分析。 2. 库存管理&#xff1a;收银系统…

Android 理解Context

文章目录 Android 理解ContextContext是什么Activity能直接new吗&#xff1f; Context结构和源码一个程序有几个ContextContext的作用Context作用域获取ContextgetApplication()和getApplicationContext()区别Context引起的内存泄露错误的单例模式View持有Activity应用正确使用…