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.hqwc.cn/news/311049.html

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

相关文章

分享一个学习Typescript最全的Github网站

一个专注研究Typescript的网站&#xff0c;&#x1f396;&#x1f396;&#x1f396;在这里你可以全面深入学习Typescript相关知识,通过动画方式讲解TS&#xff0c;还有很多常见问题解答。你还可以挑战相应的题目&#xff0c;快来学习吧 我就懒一点&#xff0c;直接原滋原味的…

Django学习3——靓号管理

目录 靓号管理 表结构和数据 根据表结构的需求&#xff0c;在models.py中创建类&#xff08;由类生成数据库中的表&#xff09; 在数据库生成表 自己在数据模拟创建一些数据&#xff1a; 靓号列表 新建靓号 编辑靓号 删除靓号 搜索靓号 靓号管理 表结构和数据 根…

Dirichlet Process (徐亦达老师)狄利克雷过程

混合高斯模型的例子 混合高斯模型 混合高斯模型&#xff08;Mixture of Gaussians&#xff0c;简称GMM&#xff09;是一种概率模型&#xff0c;用于对复杂的数据分布进行建模。它是由多个高斯分布组合而成的混合模型&#xff0c;每个高斯分布&#xff08;称为组件&#xff09;…

安全加固指南:如何更改 SSH 服务器的默认端口号

在 Linux 系统中修改 SSH 服务的默认端口号是一项重要的安全措施&#xff0c;它可以帮助增强系统的安全性。这个过程相对简单&#xff0c;但必须由具有管理员权限的用户来执行。下面&#xff0c;我将向大家介绍如何安全地更改 SSH 端口的具体步骤。 1 备份 SSH 配置文件 在修改…

nodejs+vue网上书城图书销售商城系统io69w

功能介绍 该系统将采用B/S结构模式&#xff0c;使用Vue和ElementUI框架搭建前端页面&#xff0c;后端使用Nodejs来搭建服务器&#xff0c;并使用MySQL&#xff0c;通过axios完成前后端的交互 系统的主要功能包括首页、个人中心、用户管理、图书类型管理、图书分类管理、图书信…

IntelliJ IDEA [警告] pom的依赖中出现警告Provides transitive vulnerable dependency

文章目录 1. 现象2. 为什么出现警告3. 如何对待呢4. 解决5. 解决的好处总结 1. 现象 在我们的工程 pom.xml 中的依赖中&#xff0c;所依赖的 spring-boot-starter-web 出现了警告。 依赖内容 <dependency><groupId>org.springframework.boot</groupId><…

MySQL高级SQL语句补充

目录 1.空值&#xff08;NULL&#xff09;和 无值&#xff08; &#xff09;的区别 2.正则表达式 3.存储过程 存储过程的优点 创建存储过程 调用存储过程 查看存储过程 存储过程的参数 IN 输入参数 OUT 输出参数 INOUT 输入输出参数 删除存储过程 存储过程的控制语…

最大花费金额 - 华为OD统一考试

OD统一考试 题解&#xff1a; Java / Python / C 题目描述 双十一众多商品进行打折销售&#xff0c;小明想购买自己心仪的一些物品&#xff0c;但由于受购买资金限制&#xff0c;所以他决定从众多心仪商品中购买三件&#xff0c;而且想尽可能的花完资金现在请你设计一个程序帮…

(2023,提示扩展,图像反演,文本到文本生成)自适应文本到图像生成的提示扩展

Prompt Expansion for Adaptive Text-to-Image Generation 公众&#xff1a;EDPJ&#xff08;添加 VX&#xff1a;CV_EDPJ 或直接进 Q 交流群&#xff1a;922230617 获取资料&#xff09; 目录 0. 摘要 3. 提示扩展数据集 3.1 图像审美数据集 3.2 图像到文本反演 3.3 查…

【数据结构与算法】字符串匹配(头歌习题)【合集】

目录 第1关&#xff1a;实现朴素的字符串匹配任务描述相关知识编程要求评测说明完整代码 第2关&#xff1a;实现KMP字符串匹配任务描述相关知识编程要求评测说明完整代码 第3关&#xff1a;【模板】KMP算法任务描述相关知识C STL容器string1、string的定义2、string中内容的访问…

使用YOLOv8和Grad-CAM技术生成图像热图

目录 yolov8导航 YOLOv8&#xff08;附带各种任务详细说明链接&#xff09; 概述 环境准备 代码解读 导入库 定义letterbox函数 调整尺寸和比例 计算填充 应用填充 yolov8_heatmap类定义和初始化 后处理函数 绘制检测结果 类的调用函数 热图生成细节 参数解释 we…

C++初阶——基础知识(函数重载与引用)

目录 1.命名冲突 2.命名空间 3.缺省参数 4.函数重载 1.函数重载的特点包括&#xff1a; 2.函数重载的好处包括&#xff1a; 3.引用 引用的特点包括 引用的主要用途包括 引用和指针 引用 指针 类域 命名空间域 局部域 全局域 第一个关键字 命名冲突 同一个项目之间冲…