C++_异常

       

目录

1、异常的关键字

2、异常的写法

3、异常的使用规则

3.1 规则1

3.2 规则2

3.3 规则3 

3.4 规则4

3.5 规则5 

4、异常的重新抛出 

5、异常的规范 

5.1 C++98的异常规范

5.2 C++11的异常规范

6、C++标准库的异常体系

7、异常的优缺点

结语 


前言:

        C++的异常是一种处理程序bug的方式,如今面向对象的语言基本都是用异常处理错误的,因为他可以帮助程序员快速定位错误的根源。当一个函数或者是一段代码发现了自己处理不了的错误就可以将该错误抛出,又称异常抛出,由外部的调用者或间接调用者捕获该错误,又称异常捕获

1、异常的关键字

         若要使用异常则需要用到三个关键字:try、throw、catch。他们的具体用法如下:

        try:try有属于自己的作用域,在他的作用域中存放可能会抛出异常的代码或者函数。

        throw:throw有着“抛出”的动作,throw后面跟一段内容,当程序出现错误时,throw会抛出其后面的内容(即抛出异常),作为错误的描述,通常throw后面跟一段字符串作为错误描述。

        catch:catch是捕获的意思,并且他只能捕获try里面代码的异常。当throw抛出了异常,那么就需要catch捕获该异常,不过要先捕获该异常,catch的捕获类型必须与throw抛出内容的类型一致才可以捕获。(具体看下文举例)

2、异常的写法

        异常的代码格式如下:

try
{
  // 可能会抛异常的代码
}catch( type e1 )//抛出的异常用形参e1捕获
{
  // 通常对e1的内容进行打印
}catch( type e2 )//可以有不同的捕获类型
{
  // 如上
}catch( type en )
{
  // 如上
}

        举例说明:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

int main()
{
	try
	{
		int a;
		cin >> a;
		if(a < 0)//如果输入的a小于0说明发生错误
		throw "a为负数发生错误";//抛异常:a为负数发生错误(类型是char*)
	}
	catch (const char* str)//因为抛出的因此类型是字符串,因此可以用char*接收
	{
		cout << str << endl;//打印异常的错误
	}
	return 0;
}

         运行结果:

3、异常的使用规则

3.1 规则1

        抛出异常和捕获异常的类型必须匹配,否则是捕获不了该异常的,若不匹配先会去找其他的catch能否类型匹配,若没找到则说明抛出的异常没有对应的捕获类型,则会报错。

        示例如下图:

3.2 规则2

        抛出的异常会被离该异常最近位置的catch捕获(前提是该catch与异常类型匹配),示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

void _func()
{
	int a = -2;
	if (a < 0)//如果输入的a小于0说明发生错误
		throw "a为负数发生错误";//抛异常:a为负数发生错误
}

void func()
{
	try
	{
		_func();//复用
	}
	catch (const char* str)//func函数的捕获
	{
		cout << "void func():" << str << endl;//打印异常的错误
	}
}

int main()
{
	try
	{
		func();
	}
	catch (const char* str)//main函数的捕获
	{
		cout << "main()" << str << endl;//打印异常的错误
	}
	
	return 0;
}

         运行结果:

        从结果可以看到,该异常被离他最近的func函数所捕获,假如此时的func函数的捕获类型不能与异常类型相匹配,才会让main函数中的catch去捕获。 

3.3 规则3 

        若catch捕获到了该try中的异常,则会从捕获该异常的catch之后开始执行剩下的代码(跳过其他的catch) ,示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

void _func()
{
	int a = -2;
	if (a < 0)//如果输入的a小于0说明发生错误
		throw "a为负数发生错误";//抛异常:a为负数发生错误
}

void func()
{
	_func();//复用
}

int main()
{
	try
	{
		func();
	}
	catch (const char* str)
	{
		cout << str << endl;//打印异常的错误
	}
	catch (const int* str)//若异常已经被捕获,则会跳过该catch
	{
		cout << str << endl;
	}
	cout << "继续执行catch的下面代码" << endl;
	
	return 0;
}

        运行结果:

3.4 规则4

        当一个函数栈帧中抛出了异常,则编译器先会在该栈帧中查看是否有合适的catch捕获,若没有则结束该栈帧,回到调用该函数的上一层栈帧中查看是否有合适的catch,若没有也结束当前栈帧,直到回到main函数中若还没有合适的catch则直接报错(参考规则1),该过程又叫栈回收

        具体示意图如下:

         所以,从上面可以发现,如果没有合适的catch,则程序会被直接终止且报错,为了避免这种极端情况,一般会用catch(...)来接收任意类型的异常。

         万能捕获catch(...)示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

void _func()
{
	int a = -2;
	if (a < 0)//如果输入的a小于0说明发生错误
		throw "a为负数发生错误";//抛异常:a为负数发生错误
}

void func()
{
	_func();//复用
}

int main()
{
	try
	{
		func();
	}
	catch (const int* str)
	{
		cout << str << endl;//打印异常的错误
	}
	catch (...)//万能捕获
	{
		cout << "其他类型错误" << endl;//打印异常的错误
	}
	
	return 0;
}

        运行结果:

         从结果可以看到,只要有了万能类型捕获,则无论是什么样类型的异常都不会因为没能捕获而导致程序直接被终止。

3.5 规则5 

         抛出的异常也可以是一个对象,这时候抛出的并不是对象本身,因为catch在别的作用域,所以抛出的是该对象的一个拷贝对象,并且该拷贝对象的生命周期会在catch之后销毁,所以catch的形参可以用引用接收,该形参是拷贝对象的别名而不是抛出的局部对象的别名。示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

void _func()
{
	int a = -2;
	if (a < 0)//如果输入的a小于0说明发生错误
		throw string("a为负数发生错误");//抛出的是string类型
}

void func()
{
	_func();//复用
}

int main()
{
	try
	{
		func();
	}
	//捕获的类型是string,并且可以用引用,因为返回的是拷贝对象
	catch (const string& str)
	{
		cout << str << endl;//打印异常的错误
	}
	catch (...)//万能捕获
	{
		cout << "其他类型错误" << endl;//打印异常的错误
	}

	return 0;
}

        运行结果:

4、异常的重新抛出 

        异常的重新抛出是为了解决空间资源和捕获异常同时出现的情况,上文提到捕获异常时,如果在别的函数栈帧中才捕获该异常,则编译器会结束之前的栈帧,那么如果之前的栈帧里涉及到空间资源的问题,则会引发一个问题:其他栈帧里的空间还没来得及释放就因为要捕获异常而结束了该栈帧,会导致内存泄漏的问题。

        示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

void _func()
{
	int a = -2;
	if (a < 0)//如果输入的a小于0说明发生错误
		throw string("a为负数发生错误");//抛出的是string类型
}

void func()
{
	int* arr = new int;//涉及到空间资源的管理
	try
	{
		_func();
	}
	catch (...)//函数func中必须捕获该异常才能释放申请的空间,否则直接跳到main函数了
	{
		cout << "delete arr" << endl;
		delete arr;
		throw;//重新抛出的写法
	}
	cout << "delete arr" << endl;
	delete arr;
}

int main()
{
	try
	{
		func();
	}
	//捕获的类型是string,并且可以用引用,因为返回的是拷贝对象
	catch (const string& str)
	{
		cout << str << endl;//打印异常的错误
	}
	catch (...)//万能捕获
	{
		cout << "其他类型错误" << endl;//打印异常的错误
	}

	return 0;
}

        运行结果:

5、异常的规范 

        异常的规范可以让函数的使用者清晰的看到该函数会抛出什么类型的异常或会不会抛出异常。

5.1 C++98的异常规范

        throw()是C++98的异常规范关键字,他通常写在函数接口的后面,具体用法如下:

// 这里表示这个函数会抛出int,char,char*中的某种类型的异常
void func() throw(int,char,char*);

// 这里表示这个函数不会抛出异常
void func() throw();

//在异常规范中,没有声明throw()则表示该函数可能抛出任意类型的异常
void func()

5.2 C++11的异常规范

        C++11对应异常规范新增了一个关键字:noexcept,该关键字同样写在函数的接口后面,表示该函数不会抛任何的异常,用法如下:

//表示func不会抛异常
func() noexcept;

        并且noexcept严格执行规范,若一个函数加了noexcept结果该函数会抛出异常,那么运行后就直接报错,并且复用该函数的函数也会判断为会抛出异常的函数。

        测试代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
using namespace std;

void _func()
{
	int a = -2;
	if (a < 0)//如果输入的a小于0说明发生错误
		throw string("a为负数发生错误");//抛出的是string类型
}

void func()noexcept//给func函数加上noexcept,事实上该函数会抛异常,运行后报错
{
	int* arr = new int;//涉及到空间资源的管理
	try
	{
		_func();
	}
	catch (...)//函数func中必须捕获该异常才能释放申请的空间,否则直接跳到main函数了
	{
		cout << "delete arr" << endl;
		delete arr;
		throw;//重新抛出的写法
	}
	cout << "delete arr" << endl;
	delete arr;
}

int main()
{
	try
	{
		func();
	}
	//捕获的类型是string,并且可以用引用,因为返回的是拷贝对象
	catch (const string& str)
	{
		cout << str << endl;//打印异常的错误
	}
	catch (...)//万能捕获
	{
		cout << "其他类型错误" << endl;//打印异常的错误
	}

	return 0;
}

        运行结果:

6、C++标准库的异常体系

        C++标准库中也提供了一部分异常信息可供用户捕获,以便让程序员使用时可以更好的定位错误根源。这些异常类型是以继承和多态的结构存在的,他们都有一个共同的父类exception,所以程序员只需要用该父类作为捕获的类型,然后根据多态的条件(父类指针或引用会调用子类的虚函数)就可以打印出来正确的错误信息了。

        关系图:

        具体异常信息如下图:

         标准库里的异常信息使用方式如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include<iostream>
#include<vector>
using namespace std;

int main()
{
	try {
		vector<int> v1;
		// 这里如果系统内存不够也会抛异常
		v1.reserve(1000000000);
	}
	catch (const exception& e) // 这里捕获父类对象就可以
	{
		cout << e.what() << endl;
	}
	
	return 0;
}

        运行结果:

7、异常的优缺点

         异常的优点:

        1、异常可以展现出清晰的错误信息,以便帮助程序员快速的定位错误位置。

        2、异常可以直接抛出给最外层,这样一来可以在统一的地方进行处理异常,即使复用的函数栈帧很深时也无需每层栈帧都检查一遍是否有异常。

        3、一些常用的库也存在异常的概念,如boost、gtest、gmock。

        4、有些特殊的函数不能有返回值,比如构造函数,因此不能用错误码一层层返回的方式来定位构造函数的错误,所以构造函数出了bug只能用异常发现问题。

        异常的缺点:

        1、 捕获异常时若涉及空间资源管理则稍不谨慎就会发生内存泄漏。

        2、因为抛出的异常会直接跳到main函数中,所以有些场景下调试起来不方便。

        3、异常规范并不是强制行为,因此可能会出现有些代码没加noexcept,实际上却没有抛异常的可能。

        总结:总而言之异常还是利大于弊的。

结语 

        以上就是关于C++异常的讲解,异常在程序中发挥着至关重要的作用,有了异常就能快速定位bug,帮助程序员节省了改bug的时间,最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!  

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python从0到100(四):Python中的运算符介绍

前言&#xff1a; 零基础学Python&#xff1a;Python从0到100最新最全教程。 想做这件事情很久了&#xff0c;这次我更新了自己所写过的所有博客&#xff0c;汇集成了Python从0到100&#xff0c;共一百节课&#xff0c;帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

Java中的参数传递

程序设计语言将实参传递给方法&#xff08;或函数&#xff09;的方式分为两种&#xff1a; 值传递&#xff1a;方法接收的是实参值的拷贝&#xff0c;会创建副本。引用传递&#xff1a;方法接收的直接是实参所引用的对象在堆中的地址&#xff0c;不会创建副本&#xff0c;对形…

3.1_3 连续分配管理方式

3.1_3 连续分配管理方式 连续分配&#xff1a;指为用户进程分配的必须是一个连续的内存空间。 &#xff08;一&#xff09;单一连续分配 在单一连续分配方式中&#xff0c;内存被分为系统区和用户区。 系统区通常位于内存的低地址部分&#xff0c;用于存放操作系统相关数据&am…

11 vector的实现

注意 实现仿cplus官网的的string类&#xff0c;对部分主要功能实现 实现 文件 #pragma once #include <string> #include <assert.h>namespace myvector {template <class T>class vector{public://iteratortypedef T* iterator;typedef const T* const_…

【Leetcode每日一题】 位运算 - 面试题 01.01. 判定字符是否唯一(难度⭐)(33)

1.题目解析 题目链接&#xff1a;面试题 01.01. 判定字符是否唯一 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 核心在于判断题目所给字符串是否存在相同字母&#xff0c;存在返回false即可&#xff0c;不存在返回true即可。 …

光电容积脉搏波PPG信号分析笔记

1.脉搏波信号的PRV分析 各类分析参数记参数 意义 公式 参数意义 线性分析 时域分析 均值MEAN 反应RR间期的平均水平 总体标准差SDNN 评估24小时长程HRV的总体变化&#xff0c; SDNN &#xff1c; 50ms 为异常&#xff0c;SDNN&#xff1e;100ms 为正常&#xff1b;…

灵魂指针,教给(三)

欢迎来到白刘的领域 Miracle_86.-CSDN博客 系列专栏 C语言知识 先赞后看&#xff0c;已成习惯 创作不易&#xff0c;多多支持&#xff01; 目录 一、 字符指针变量 二、数组指针变量 2.1 数组指针变量是什么 2.2 数组指针变量如何初始化 三、二维数组传参本质 四、函数…

如何在Linux系统安装SVN并配置固定公网地址远程访问【内网穿透】

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

qsort函数

目录 1.qsort函数是什么 1.1qsort函数的原型 2.qsort函数的使用 2.1使用qsort函数排序整型数据 2.2使用qsort排序结构数据 3.qsort函数的模拟实现 1.qsort函数是什么 很多小伙伴们都没有听说过qsort这个函数&#xff0c;qsort函数是C语言标准库中的一个排序函数&#xf…

前端精准测试调用链路分析

精准测试在评估需求的测试范围时&#xff0c;需要评估一下代码的影响范围&#xff0c;这个范围有两部分&#xff1a;一是需求直接修改的代码&#xff1b;二是修改代码影响到的功能模块。代码影响到的功能一般是通过调用链路分析来实现的&#xff0c;java和kotlin代码可以由java…

【Java从入门到精通】Java异常处理

异常是程序中的一些错误&#xff0c;但并不是所有的错误都是异常&#xff0c;并且错误有时候是可以避免的。 比如说&#xff0c;你的代码少了一个分号&#xff0c;那么运行出来结果是提示是错误 java.lang.Error&#xff1b;如果你用System.out.println(11/0)&#xff0c;那么…

每日OJ题_路径dp②_力扣63. 不同路径 II

目录 力扣63. 不同路径 II 解析代码 力扣63. 不同路径 II 63. 不同路径 II 难度 中等 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;…

week06 day04 (数据库高级函数 procedure 、sql写函数)

一. ER模型 矩形&#xff1a; 代表实体椭圆&#xff1a;代表实体的属性菱形&#xff1a;relation 代表实体之间的关系 二. 存储过程&#xff08;procedure&#xff09; 1. 语法 语法: create procedure 存储过程名(参数,…) begin//代码 end// 注意&#xff1a; 因为在存储…

C语言 —— 图形打印

题目1&#xff1a; 思路&#xff1a; 如果我们要打印一个实心正方形&#xff0c;其实就是一个二维数组&#xff0c;i控制行&#xff0c;j控制列&#xff0c;行列不需要控制&#xff0c;arr[i][j]直接打印星号即可。 对于空心正方形&#xff0c;我们只需要控制行和列的条件&…

MyBatis学习笔记|2024最新版Mybatis

Mybatis简介 MyBatis历史 MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下,iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到GithubiBatis一词来源于“internet”和“aba…

应对电子邮件危害:垃圾邮件的特征、影响和预防策略

垃圾邮件是一种侵入性的未经请求的电子通信形式&#xff0c;主要通过电子邮件传播。它将大量无关、欺骗性或恶意内容发送给邮箱用户&#xff0c;内容涵盖令人讨厌的广告和欺诈行为、以及网络钓鱼链接。 截至 2022 年 12 月&#xff0c;垃圾邮件占电子邮件流量的 45% 以上。除了…

数据结构---C语言栈队列

知识点&#xff1a; 栈&#xff1a; 只允许在一端进行插入或删除操作的线性表&#xff0c;先进后出LIFO 类似一摞书&#xff0c;按顺序拿&#xff0c;先放的书只能最后拿&#xff1b; 顺序栈&#xff1a;栈的顺序存储 typedef struct{Elemtype data[50];int top; }SqStack; SqS…

Window部署Oracle并实现公网环境远程访问本地数据库

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…

掌握Redis,看完这篇文章就够了!

目录 1.Redis介绍 2.Redis服务器与客户端 3.Redis配置文件 4.Redis数据类型操作 4.1使用python连接数据库 4.2 字符串 4.3 哈希 4.4 键对应操作 4.5 列表 4.6 集合 4.7 有序集合 1.Redis介绍 Redis 是一个开源的内存数据库&#xff0c;它提供了一个高性能的 key-val…

基于PyTorch深度学习实战入门系列-Numpy基础全

Numpy的使用 导入Numpy模块 import numpy as np创建数组&#xff08;一维数组、小数数组、二维数组&#xff09; # 创建一个一维数组 n1 np.array([1, 2, 3]) # 创建一个含有小数的一维数组 n2 np.array([0.1, 0.2, 0.3]) # 创建一个简单的二维数组 n3 np.array([[1, 2], [3…