C++ 异常捕获详解

本文详细介绍了C++中的异常处理机制,包括异常的分类、如何使用try、catch和throw关键字进行异常捕获和处理。通过实例展示了单类型、多类型以及全部类型异常的捕获方式,并探讨了C++标准异常及其派生类。异常处理是确保程序健壮性的重要手段,能够优雅地处理运行时错误,避免程序崩溃。
摘要由CSDN通过智能技术生成

为什么存在异常处理

在程序运行时常会碰到一些错误,例如除数为 0、年龄为负数、数组下标越界等,这些运行时错误如果放任不管,系统就会执行默认的操作,终止程序运行,也就是我们常说的程序崩溃(Crash)。C++ 提供了异常(Exception)机制,让我们能够捕获运行时错误,给程序一次“起死回生”的机会,或者至少告诉用户发生了什么再终止程序。

而 C++ 异常处理机制就可以让我们捕获并处理这些错误,然后我们可以让程序沿着一条不会出错的路径继续执行,或者不得不结束程序,但在结束前可以做一些必要的工作,例如将内存中的数据写入文件、关闭打开的文件、释放分配的内存等。

程序的错误的三种分类

程序的错误大致可以分为三种,分别是语法错误、逻辑错误和运行时错误:

  1. 语法错误在编译和链接阶段就能发现,只有 100% 符合语法规则的代码才能生成可执行程序。语法错误是最容易发现、最容易定位、最容易排除的错误,程序员最不需要担心的就是这种错误。

  2. 逻辑错误是说我们编写的代码思路有问题,不能够达到最终的目标,这种错误可以通过调试来解决。

  3. 运行时错误是指程序在运行期间发生的错误,例如除数为 0、内存分配失败、数组越界、文件不存在等。C++ 异常(Exception)机制就是为解决运行时错误而引入的。

捕获异常的关键字和格式

异常提供了一种转移程序控制权的方式。C++ 异常处理涉及到三个关键字:try、catch、throw

throw: 当问题出现,程序通过throw抛出一个异常。
catch: 在你想要处理问题的地方,通过异常处理程序捕获异常。
try: try 块中的代码标识将被激活的特定异常。它后面允许跟着一个或多个 catch 块。

1.单类型异常捕获

try { 
	//保护区,就是可能会出错的代码
}

catch( ExceptionName e1 ) {

// 出错后,通过异常处理程序捕获异常
}

2.多类型异常捕获(使用多个catch捕获不同类型的异常)

try { 
	//保护区,就是可能会出错的代码
}

catch( ExceptionName e1 ) {

// 出错后,通过异常处理程序捕获异常
//如果try在不同场景会抛出不同异常,此时可尝试罗列多个 catch 语句,用于捕获不同类型异常
}
catch( ExceptionName e2 ) {
// catch 块
}
catch( ExceptionName eN ) {
// catch 块
}

3.全部类型捕获(缺点是你无法知道错误类型,只知道发送了错误)

try{
	//保护区
}
catch(...)  //这里使用...,表示会捕获所有类型的异常都会
{
	// catch 块
}

举例使用

/**********		单catch捕获		****************/

#include<iostream>
#include<exception>
#include<string>
using namespace std;

int Division(int& a, int& b)
{
	if (b == 0)
		throw "除数不能为0";

	return a / b;
}

int main()
{
	int m = 4, n = 0, res;
	try {
		res = Division(m, n);
		cout << res << endl;
	}catch(const char* e){
		cout << e << endl;
	}
	return 0;
}

/**********		多catch捕获		****************/

#include<iostream>
#include<exception>
#include<string>
using namespace std;

int Division(int& a, int& b)
{
	string sub = "除数不能为0";
	if (b == 0)		//如果除数为0,throw一个string
		throw sub;
	else if (b < 0)	//如果除数为负数,throw一个const char*
		throw "除数不允许为负数";
	return a / b;
}

int main()
{
	//int m = 4, n = 2, res;
	//int m = 4, n = 0, res;
	//int m = 4, n = -1, res;
	try {
		res = Division(m, n);
		res = Division(m, n);
		res = Division(m, n);
		cout << res << endl;
	}catch(string e1){	// Exception is: 除数不能为0
		cout << "Exception is: " << e1 << endl;
	}
	catch (const char* e2){// Exception is: 除数不允许为负数
		cout << "Exception is: " << e2 << endl;
	}
	return 0;
}

C++ 标准异常

C++语言本身或者标准库抛出的异常都是 exception 的子类,称为标准异常(Standard Exception)。你可以通过下面的语句来捕获所有的标准异常:

try{
    //可能抛出异常的语句
}catch(exception &e){
    //处理异常的语句
}

之所以使用引用,是为了提高效率。如果不使用引用,就要经历一次对象拷贝(要调用拷贝构造函数)的过程。

exception 类位于 头文件中,它被声明为:

class exception{
public:
    exception () throw();  //构造函数
    exception (const exception&) throw();  //拷贝构造函数
    exception& operator= (const exception&) throw();  //运算符重载
    virtual ~exception() throw();  //虚析构函数
    virtual const char* what() const throw();  //虚函数
}

这里需要说明的是 what() 函数。what() 函数返回一个能识别异常的字符串,正如它的名字“what”一样,可以粗略地告诉你这是什么异常。不过C++标准并没有规定这个字符串的格式,各个编译器的实现也不同,所以 what() 的返回值仅供参考。

下图展示了 exception 类的继承层次:
在这里插入图片描述
exception 类的直接派生类:

异常名称说明
logic_error逻辑错误
runtime_error运行时错误
bad_alloc使用 new 或 new[ ] 分配内存失败时抛出的异常
bad_typeid使用 typeid 操作一个 NULL 指针,而且该指针是带有虚函数的类,这时抛出 bad_typeid 异常
bad_cast使用 dynamic_cast 转换失败时抛出的异常
ios_base::failureio 过程中出现的异常
bad_exception这是个特殊的异常,如果函数的异常列表里声明了 bad_exception 异常,当函数内部抛出了异常列表中没有的异常时,如果调用的 unexpected() 函数中抛出了异常,不论什么类型,都会被替换为 bad_exception 类型

logic_error 的派生类:

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

runtime_error 的派生类:

异常名称说明
range_error计算结果超出了有意义的值域范围
overflow_error算术计算上溢
underflow_error算术计算下溢
  • 15
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值