【C++基础】异常匹配与内建异常类

101 阅读3分钟

目录

异常匹配

catch: 按异常类型匹配

已知try是监视可能出现问题的代码,throw是扔出问题,catch是看扔出的问题是否和它的类型匹配,如果是匹配那么就抓住问题进行处理。
这里的类型使用传引用,避免拷贝浪费。

catch ( ExceptionType& parameter ) { /* 处理异常 */ }

若try{}中所抛异常类型与catch()的参数类型(ExceptionType)匹配,则进入catch块
若对异常对象的内容不感兴趣,可省略catch参数,只保留类型。
如:

catch ( ExceptionType& ) { /* 处理异常 */ }

举例:
f1()抛出整型异常。
f2()是要申请28G内存,如果电脑内存不够,执行代码会出错,抛出bad_alloc异常。这是C++规定的,new出现问题就会抛出bad_alloc异常。bad_alloc是C++异常类exception基类的派生类。

void f1() { throw 100; }
void f2() { for (int i = 1; i <= 100; i++) new int[70000000]; }

对于f1的两种处理:

try { f1(); }
catch (int) { //仅有类型
  cout << "Error" << endl;
}

try { f1(); }
catch (int& e) { //类型+参数
  cout << e << endl;
}

对于f2的两种处理:
注意e.what()是exception类内部的一个虚函数,返回的是一个指针,指向一个字符串,包含了异常的解释信息。


try { f2(); } 
catch (bad_alloc) {
  cout << "new failed" << endl;
}
try { f2(); } 
catch (bad_alloc &e) {
  cout << "Exception: " << e.what() << endl;
}

为何要使用异常类

整数作为异常类型,只能获取整数的信息。
不使用异常类,则捕获的异常所能传递的信息量很少。

try {
  // ...
}
catch (int e) {
  //do something with e
}

使用异常类,则可以在抛出异常时传递很多信息,在捕获异常时接收这些信息。

class Beautiful {
  string name;
  string hometown;
  int    salary;
  string misdoing;
// ………… 
}
try {
  // ...
}
catch (Beautiful object) {
  //do something with object
}

内建异常类

标准库中的异常基类

exception 是标准库所有异常类的基类
使用异常类需要包括头文件 #include <exception>
class exception需要注意的部分如下:

exception(); // 构造函数
virtual const char* what(); //返回解释性字符串
what()返回的指针指向拥有解释信息的空终止字符串的指针。该指针保证在获取它的异常对象被销毁前,或在调用该异常对象的非静态成员函数前合法

标准库中的异常类

在使用所有标准库异常类的时候,都必须附加std名字空间。
在这里插入图片描述
runtime_error与logic_error的使用区别
来自wiki的解释:

逻辑错误:(有时称为语义错误)是程序中的一个错误,它会导致程序错误运行,但不会异常终止(或崩溃)。逻辑错误会产生非预期或不希望的输出或其他行为,尽管它可能不会立即被识别。
编译语言和解释语言都会出现逻辑错误。与有语法错误的程序不同,有逻辑错误的程序在语言中是有效的程序,尽管它的行为不符合预期。
运行时错误:一个错误在程序执行过程中发生的。相反,编译时错误发生在程序编译时。运行时错误表示程序中的错误或设计人员预期的问题,但却无能为力。例如,内存不足通常会导致运行时错误。

例1:vector下标访问越界out_of_range异常

vector有许多种访问元素的方式,其中[]下标访问不会做越界检查,at()会做越界检查。
在这里插入图片描述
在这里插入图片描述
我们将它截获
在这里插入图片描述

例2:内存分配失败bad_alloc异常

#include<iostream>
using namespace std;
int main()
{
	for( int i = 0; i < 10000; i++)
	{
		auto *p = new long long int [70000];
		cout << i << "array" << endl;
	}
	return 0;
}

在这里插入图片描述
进行捕获

#include<iostream>
#include <exception>
#include <stdexcept>
#include <new>
using namespace std;
int main()
{
	try 
	{
		for (int i = 0; i < 10000; i++)
		{
			auto* p = new long long int[70000];
			cout << i << "array" << endl;
		}
	}
	catch (bad_alloc& e)
	{
		cout << "exception " << e.what() << endl;
	}
	return 0;
}

exception bad allocation程序正常退出。

例3:侧向转换失败bad_cast异常类

创建基类student
创建派生类Undergraduate和Graduate
主函数中用dynamic_cast 将Undergraduate类型转换为Graduate类型.
dynamic_cast只有在做引用类型转换时候转换失败才会抛出异常。使用指针类型时,如果转换失败会返回nullptr

#include <iostream>
#include <exception>
#include <stdexcept>

using namespace std;

class Student {
public:
	Student() = default;
	virtual void foo() {};
};
class Undergraduate : public Student {};
class Graduate : public Student {};
int main()
{
	Undergraduate u;
	Graduate g;
	Student *s1 = &u;
	Student *s2 = &g;
	//正常操作,p指针不为nullptr
	Graduate *p = dynamic_cast<Graduate *>(s2);
	//转化不成功,p2指针为nullptr
	Graduate *p2 = dynamic_cast<Graduate *>(s1);
	if(p2 == nullptr){
		cout << "cast s1 to Graduate* failed " << endl;
	}
	else{
		cout << "cast s1 to Graduate* succeeded " << endl;
	}
	//引用,抛异常
	try{
		Graduate &r1 = dynamic_cast<Graduate &> (u);
	}
	catch (bad_cast & e){
		cout << "Exception: " << e.what() << endl;
	}
	return 0;
}

在这里插入图片描述

几种情况,使用对应异常

1、除法运算中除数为0;
:invalid_argument

2、在只读文件系统中以写模式打开一个文件流;
:filesystem::filesystem_error
3、 数组a的容量为5,向 a[5] 中写入数据;
:out_of_range

4、用 new 申请内存失败;
:bad_array_new_length
5、将A指针类型转换为B指针类型失败;
:bad_any_cast
6、将 1234567 这个整数存到一个 short int 类型的变量中(在32位C++编译器中编译)
:length_error