C++异常

异常无处不在!尽管程序是你设计的,但是却不一定按你的设想运行.面对随时有可能出现的异常情况(俗称程序跑飞了),你该如何应对?C++提出了异常机制.

什么是异常?

异常是一种程序控制机制,与函数机制互补.函数是一种以栈结构展开的上下函数衔接的程序控制系统,异常是另一种控制结构,它可以在出现"意外"时中断当前函数,并以某种机制(类型匹配)回馈给隔代的调用者相关的信息.

在我以前的文章里,常常讲到防御式编程,即考虑可能出现的非正常情况(比如传入函数必须是正值,却传入了负值),并返回一些有意义的值来及时退出函数而不是让它继续执行.但是这样极其麻烦,比如在函数调用者处还要根据返回值做相应处理,如果调用的层次多了还得依次分层返回.C++异常很好的处理了这个问题.
在这里插入图片描述
异常处理就好像直通车一样,不必依次返回.

异常的语法

  1. 异常发生现场,使用throw抛出异常.表达式可以是任何类型,比如你可以抛出一个整数,可以抛出一串字符串,还可以抛出一个自定义的类.(是不是比返回一个固定类型的值好多了?)
void function(){
    throw 表达式;
}
  1. 异常处理点,使用try把可能抛出异常的程序块包含起来,用catch捕捉异常并处理.从上至下匹配,且只会执行其中一条catch语句,如果没有找到匹配,则会自动调用abort结束程序.
try{
    //其他程序
    function();
    //其他程序
}catch(异常类型){
    //处理代码
}catch(异常类型 形参){
    //处理代码
}catch(...){//所有其他异常类型
    //处理代码
}

catch中处理不了的异常,可以继续throw向调用者抛出.try中的内容称为保护块.

异常接口声明

为增加程序的可读性,可以在函数声明中列出可能抛出的所有异常类型.

void function()throw(int, string, double) {//表示可能会抛出整数类型,字符串类型,浮点类型的异常
    //...
}

如果抛出了未包含在异常声明内的其他类型异常,则可能会导致程序终止.
如果没有异常接口声明,则会抛出任何异常.
如果不想抛出任何异常,使用throw()声明.

异常的使用

  1. 基本数据类型
#include <iostream>
#include <string>
#include <math.h>
using namespace std;

int mySqrt(int num) {
	if (num < 0) {
		throw -1;
	}

	printf("函数正常结束.");
	return sqrt(num);
}

int main(void) {
	try {
		cout << "开始测试" << endl;
		mySqrt(-1);
		cout << "函数正常结束!" << endl;
	}
	catch (int error) {
		cout << "抛出了整数类型异常:" << error << endl;
	}
	catch (...) {
		cout << "其他类型的异常" << endl;
	}

	system("pause");
}

在这里插入图片描述

  1. 字符串类型
#include <iostream>
#include <string>
#include <math.h>
using namespace std;

int mySqrt(int num) {
	if (num < 0) {
		throw -1;
	}
	else if (sqrt(num) * sqrt(num) != num) {
		throw string("该数开方后不是整数!!!");
	}

	printf("函数正常结束.");
	return sqrt(num);
}

int main(void) {
	try {
		cout << "开始测试" << endl;
		mySqrt(3);
		cout << "函数正常结束!" << endl;
	}
	catch (int error) {
		cout << "抛出了整数类型异常:" << error << endl;
	}
	catch (string error) {//string& error也可以
		cout << "抛出了字符串类型异常:" << error << endl;
	}
	catch (...) {
		cout << "其他类型的异常" << endl;
	}

	system("pause");
}

在这里插入图片描述
修饰指针的const需严格匹配,比如下面程序与预料不符.

#include <iostream>
#include <string>
#include <math.h>
using namespace std;

int mySqrt(int num) {
	if (num < 0) {
		throw -1;
	}
	else if (sqrt(num) * sqrt(num) != num) {
		const char* words = "该数开方后不是整数!!!";
		throw words;
	}

	printf("函数正常结束.");
	return sqrt(num);
}

int main(void) {
	try {
		cout << "开始测试" << endl;
		mySqrt(3);
		cout << "函数正常结束!" << endl;
	}
	catch (int error) {
		cout << "抛出了整数类型异常:" << error << endl;
	}
	catch (string& error) {
		cout << "抛出了字符串类型异常:" << error << endl;
	}
	catch (char* error) {//加上const才会进入该语句!!!
		cout << "抛出了字符串类型(char*)异常:" << error << endl;
	}
	catch (...) {
		cout << "其他类型的异常" << endl;
	}

	system("pause");
}

在这里插入图片描述
3. 类类型

#include <iostream>
#include <string>
#include <math.h>
using namespace std;

class myException {
public:
	myException(int num) {
		this->num = num;
		id = 1;
		cout << "调用了构造函数,ID = " << id << endl;
	}
	myException(const myException& c) { //拷贝构造函数
		num = c.num;
		id = c.id + 1;
		cout << "调用了拷贝构造函数,ID = " << id << endl;
	}
	~myException() {
		cout << "调用了析构函数,ID = " << id << endl;
	}

	void print() {
		cout << "数字太大,输入的数字:" << num << endl;
	}
private:
	int num;
	int id;
};

int mySqrt(int num) {
	if (num < 0) {
		throw -1;
	}
	else if (sqrt(num) * sqrt(num) != num) {
		const char* words = "该数开方后不是整数!!!";
		throw words;
	}
	else if (num > 100000) {
		myException exception(num);//局部变量,id = 1
		throw exception;//通过拷贝构造函数生成了一个全局变量,id = 2
	}

	printf("函数正常结束.");
	return sqrt(num);
}

int main(void) {
	try {
		cout << "开始测试" << endl;
		mySqrt(1000000);
		cout << "函数正常结束!" << endl;
	}
	catch (int error) {
		cout << "抛出了整数类型异常:" << error << endl;
	}
	catch (string& error) {
		cout << "抛出了字符串类型异常:" << error << endl;
	}
	catch (const char* error) {
		cout << "抛出了字符串类型(const char*)异常:" << error << endl;
	}
	catch (myException error) {//使用拷贝构造函数(传参),id = 3
		cout << "抛出了自定义类类型异常:";
		error.print();
	}
	catch (...) {
		cout << "其他类型的异常" << endl;
	}

	system("pause");
}

在这里插入图片描述
注意到这里调用了两次拷贝构造函数,效率不高,使用匿名类可以少调用一次,catch内加引用可以再少调用一次.
修改后版本:

#include <iostream>
#include <string>
#include <math.h>
using namespace std;

class myException {
public:
	myException(int num) {
		this->num = num;
		id = 1;
		cout << "调用了构造函数,ID = " << id << endl;
	}
	myException(const myException& c) { //拷贝构造函数
		num = c.num;
		id = c.id + 1;
		cout << "调用了拷贝构造函数,ID = " << id << endl;
	}
	~myException() {
		cout << "调用了析构函数,ID = " << id << endl;
	}

	void print() {
		cout << "数字太大,输入的数字:" << num << endl;
	}
private:
	int num;
	int id;
};

int mySqrt(int num) {
	if (num < 0) {
		throw -1;
	}
	else if (sqrt(num) * sqrt(num) != num) {
		const char* words = "该数开方后不是整数!!!";
		throw words;
	}
	else if (num > 100000) {
		throw myException(num);//使用匿名类提高效率
	}

	printf("函数正常结束.");
	return sqrt(num);
}

int main(void) {
	try {
		cout << "开始测试" << endl;
		mySqrt(1000000);
		cout << "函数正常结束!" << endl;
	}
	catch (int error) {
		cout << "抛出了整数类型异常:" << error << endl;
	}
	catch (string& error) {
		cout << "抛出了字符串类型异常:" << error << endl;
	}
	catch (const char* error) {
		cout << "抛出了字符串类型(const char*)异常:" << error << endl;
	}
	catch (myException& error) {//使用引用接参提高效率
		cout << "抛出了自定义类类型异常:";
		error.print();
	}
	catch (...) {
		cout << "其他类型的异常" << endl;
	}

	system("pause");
}

在这里插入图片描述
细心的读者可以发现函数正常结束(两处)这句话一直都没有打印出来过.

异常也是类,拥有类的特性,有继承,多态,虚函数等等.

标准库的异常类

在这里插入图片描述

异常类头文件异常的含义
bad_allocexception用new动态分配空间失败
bad_castnew执行dynamic_cast失败
bad_typeidtypeinfo对某个空指针p执行typeid(* p)
bad_exceptiontypeinfo当某个函数fun()因在执行过程中抛出了异常声明所不允许的异常而调用unexpected()函数时,若unexpected()函数又一次抛出了fun()的异常声明所不允许的异常,且fun()的异常声明列表中有bad_exception,则会有一个bad_exception异常在fun()的调用点被抛出
ios_base::failureios用来表示C++的输入输出流执行过程中发生的错误
underflow_errorstdexcept算术运算时向下溢出
overflow_errorstdexcept算术运算时向上溢出
range_errorstdexcept内部计算时发生作用域的错误
out_of_rangestdexcept表示一个参数值不在允许的范围之内
length_errorstdexcept尝试创建一个长度超过最大允许值的对象
invalid_argumentstdexcept表示向函数传入无效参数
domain_errorstdexcept执行一段程序所需要的先决条件不满足

最后

一些语言规定了只能抛某个类的派生类,比如Java规定抛掷的类必须派生自Exception类.C++没有强制的规定,不过并不妨碍我们按照这样的规则使用.因为使用标准库提供的异常类,或是根据这些类派生出新的类会让我们方便很多.比如logic_error,runtime_error及其派生类都有传字符串的构造函数(通过它传错误信息)及what()函数(通过它得到错误信息).

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值