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.rhkb.cn/news/227329.html

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

相关文章

swing快速入门(三十二)消息对话框

注释很详细&#xff0c;直接上代码 上一篇 新增内容 1.自定义对话框前列图标 2.消息对话框的若干种形式 package swing21_30;import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent;public class swing_test_30 {// 定义一个JFrameJFrame jFrame n…

Redis内存使用率高,内存不足问题排查和解决

问题现象 表面现象是系统登录突然失效&#xff0c;排查原因发现&#xff0c;使用redis查询用户信息异常&#xff0c;从而定位到redis问题 if (PassWord.equals(dbPassWord)) {map.put("rtn", 1);map.put("value", validUser);session.setAttribute("…

初识智慧城市

文章目录 智慧家居 智慧社区 智慧交通 智慧医疗 智慧教育 智慧旅游 智慧农业 智慧安防 智慧家居 利用智能语音、智能交互等技术,实现用户对家居系统各设备的远程操控和能控制如开关窗帘(窗户)、操控家用电器和照明系统、打扫卫生等操作。利用计算机视觉等技术,对被照看…

java爬虫(jsoup)如何设置HTTP代理ip爬数据

目录 前言 什么是HTTP代理IP 使用Jsoup设置HTTP代理IP的步骤 1. 导入Jsoup依赖 2. 创建HttpProxy类 3. 设置代理服务器 4. 使用Jsoup进行爬取 结论 前言 在Java中使用Jsoup进行网络爬虫操作时&#xff0c;有时需要使用HTTP代理IP来爬取数据。本文将介绍如何使用Jsoup设…

智能监控平台/视频共享融合系统EasyCVR海康设备国标GB28181接入流程

TSINGSEE青犀视频监控汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安防视频监控的能力&…

sqlilabs第三十二三十三关

Less-32&#xff08;GET - Bypass custom filter adding slashes to dangerous chars) 手工注入 由 宽字符注入可知payload 成功触发报错 http://192.168.21.149/Less-32/ ?id1%df 要写字符串的话直接吧字符串变成ascii码 注意16进制的表示方式 自动注入 sqlmap -u http:…

饥荒Mod 开发(二二):显示物品信息

饥荒Mod 开发(二一)&#xff1a;超大便携背包&#xff0c;超大物品栏&#xff0c;永久保鲜 饥荒Mod 开发(二三)&#xff1a;显示物品栏详细信息 饥荒中的物品没有详细信息&#xff0c;基本上只有一个名字&#xff0c;所以很多物品的功能都不知道&#xff0c;比如浆果吃了也不知…

鸿蒙4.0实战教学—基础ArkTS(简易视频播放器)

构建主界面 主界面由视频轮播模块和多个视频列表模块组成&#xff0c;效果图如图&#xff1a; VideoData.ets中定义的视频轮播图数组SWIPER_VIDEOS和视频列表图片数组HORIZONTAL_VIDEOS。 // VideoData.ets import { HorizontalVideoItem } from ./HorizontalVideoItem; impo…

35.搜索插入位置

给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2示例 2: 输入:…

大开眼界,速看!Solid Edge各版本安装指南

下载链接 https://pan.baidu.com/s/1g3QEGoLsjD7JaudZUOW96Q?pwd0531 1.鼠标右击【Solid Edge2024(64bit)】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;【解压到 Solid Edge2024(64bit)】。 2.打开解压后的文件夹&#xff0c;双击打开【Setup】文…

【前端技术】LocalForage数据存储

✨专栏介绍 在当今数字化时代&#xff0c;Web应用程序已经成为了人们生活和工作中不可或缺的一部分。而要构建出令人印象深刻且功能强大的Web应用程序&#xff0c;就需要掌握一系列前端技术。前端技术涵盖了HTML、CSS和JavaScript等核心技术&#xff0c;以及各种框架、库和工具…

算法训练营Day26

#Java #全排列 #回溯 开源学习资料 Feeling and experiences&#xff1a; 递增子序列&#xff1a;力扣题目链接 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组…

《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识(18)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第I篇 第1章 PCI总线的基本知识&#xff08;17&#xff09; 1.4 PCI总线的中断机制 1.4.2 中断信号与PCI总线的连接关系 在PCI总线中&#xff0c;INTx信号属于边带信号。所谓边带信号是指这些信号在PCI总线环境…

深入了解云原生:定义与特征解析

文章目录 一、云原生概述1.1 什么是云原生1.2 云原生组成要素1.3 补充资料 二、云原生的目标2.1 云原生关键目标2.2 云原生特性 三、云原生应用 VS 传统单体应用参考资料 一、云原生概述 1.1 什么是云原生 (1)云原生定义 云原生(Cloud Native) 是一种软件架构和开发方法论&a…

二叉树顺序结构与堆的概念及性质(c语言实现堆)

上次介绍了树&#xff0c;二叉树的基本概念结构及性质&#xff1a;二叉树数据结构&#xff1a;深入了解二叉树的概念、特性与结构 今天带来的是&#xff1a;二叉树顺序结构与堆的概念及性质&#xff0c;还会用c语言来实现堆 文章目录 1. 二叉树的顺序结构2.堆的概念和结构3.堆…

Vue : 监视属性

目录 一个案例 监听属性 handler immediate vm.$watch(xxx) 深度监视 监视的简写 computed和watch之间的区别 一个案例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport"…

使用TLS/SSL Pinning保护安卓应用程序

使用TLS/SSL Pinning保护安卓应用程序 在现代术语中&#xff0c;“SSL”&#xff08;安全套接层&#xff09;通常指的是“TLS”&#xff08;传输层安全&#xff09;。虽然 SSL 和 TLS 不是同一个东西&#xff0c;但 TLS 是 SSL 的改进和更安全的版本&#xff0c;并且在实践中已…

前后端分离nodejs+vue+ElementUi网上订餐系统69b9

课题主要分为两大模块&#xff1a;即管理员模块和用户模块&#xff0c;主要功能包括个人中心、用户管理、菜品类型管理、菜品信息管理、留言反馈、在线交流、系统管理、订单管理等&#xff1b; 运行软件:vscode 前端nodejsvueElementUi 语言 node.js 框架&#xff1a;Express/k…

超详细YOLOv8姿态检测全程概述:环境、训练、验证与预测详解

目录 yolov8导航 YOLOv8&#xff08;附带各种任务详细说明链接&#xff09; 搭建环境说明 不同版本模型性能对比 不同版本对比 参数解释 模型解释 训练 训练示意代码 训练数据与.yaml配置方法 .yaml配置 数据集路径 标签数据说明 训练参数说明 训练过程示意及输出…

集群部署篇--Redis 主从模式

文章目录 前言Redis 主从部署&#xff1a;1.1 主从架构 介绍&#xff1a;1.2 主从架构 实现&#xff1a;1.2.1 redis 安装&#xff1a; 1.3 主从架构优缺点&#xff1a;1.4 故障转移&#xff1a; 总结 前言 显然在线上环境中 Redis 服务不能以单机的方式运行&#xff0c;必须有…