[cpp--->异常]

一、异常的概念

异常是一种处理错误的方式,当一个函数出现无法处理的错误时,可以抛出异常,函数的直接调用或者间接调用者捕获并处理错误.
throw关键字可以用来抛出异常
使用try catch组合捕获异常,try块儿为可能会抛出异常的代码块,catch为异常处理模块.

try
{
 // 保护的标识代码
}catch( ExceptionName e1 )
{
 // catch 块
}catch( ExceptionName e2 )
{
 // catch 块
}catch( ExceptionName eN )
{
 // catch 块
}

二、异常的使用

1.异常是根据抛出的对象引发的,激活那一个catch处理代码是根据抛出的对象类型确定的.

#include<iostream>
using namespace std;
double Division(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)
		throw "Division by zero condition!";//抛出的对象类型是const char*
	else
		return ((double)a / (double)b);
}

void Func()
{
	int len, time;
	cin >> len >> time;
	//1.func是异常模块儿的直接调用者
	try
	{
		cout << Division(len, time) << endl;
	}
	catch (char str)//捕获的异常对象类型是char
	{
		cout << str << endl;
	}

	cout << "void Func()" << endl;
}

int main()
{
	//1.main是异常模块儿的间接调用者
	try
	{
		Func();
	}
	catch (const char* str)//捕获异常对象的类型是const char*
	{
		cout << str << endl;
	}

	return 0;
}

2.catch捕获对象类型相同,优先激活调用距离被调用异常函数最近的那一个catch代码块儿

#include<iostream>
using namespace std;
double Division(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)
		throw "Division by zero condition!";
	else
		return ((double)a / (double)b);
}

void Func()
{
	int len, time;
	cin >> len >> time;
	//1.func是异常模块儿的直接调用者
	try
	{
		cout << Division(len, time) << endl;
	}
	catch (const char* str)
	{
		cout << str <<"func" << endl;
	}

	cout << "void Func()" << endl;
}

int main()
{
	//1.main是异常模块儿的间接调用者
	try
	{
		Func();
	}
	catch (const char*str)
	{
		cout << str<<"main" << endl;
	}

	return 0;
}

3,throw抛出异常对象后会生成一个临时对象,这里临时对象会在catch以后销毁,这是因为throw抛出的可能会是一个临时对象,处理作用域就销毁了,而throw往往跟catch不在一个作用域.这类似于传值返回,所以catch是不能用引用来做形参的.

4.catch(…)可以捕获任意类型的异常,问题是不知道异常错误是什么,通常用一个异常类封装错误代码和错误信息,抛出异常的时候抛出类对象,捕获异常打印异常信息只需接输出对象成员变量即可.

#include<iostream>
using namespace std;
class Exception
{
public:
	Exception(int errid, const string& err_message)
		:_errid(errid),_err_message(err_message){}
	const int& getErrid()const { return _errid; }
	const string& getErrMessage()const{ return _err_message; }
protected:
	int _errid;
	string _err_message;
};
double Division(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)throw Exception(1, "除0错误");
	else return ((double)a / (double)b);
}

int main()
{
	try{Division(1, 0);}
	catch (const Exception err){cout << err.getErrMessage()<<"main" << endl;}
	return 0;
}

5.在函数调用链中,异常匹配原则是查看throw本身是否直接在try代码块儿中,如果在查找对应的catch代码块儿处理错误,如果不在则跳出当前的函数栈,到调用该函数的函数栈中查找匹配的catch,如此重复上述动作,如果一直到main都没有匹配的catch代码块,则程序退出.如果有匹配的catch,catch执行完之后会执行catch后面的代码.

6.异常有可能因为一些原因没有被捕获到,造成程序退出,影响其他代码块儿的运行,可以使用任意类型异常捕获,一般将捕获任意类型的catch放在最后防止意外.

#include<iostream>
using namespace std;
class Exception
{
public:
	Exception(int errid, const string& err_message)
		:_errid(errid),_err_message(err_message){}
	const int& getErrid()const { return _errid; }
	const string& getErrMessage()const{ return _err_message; }
protected:
	int _errid;
	string _err_message;
};
double Division(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0)
		throw Exception(1,"除0异常");
	else
		return ((double)a / (double)b);
}
int sub(int a, int b)
{
	throw 1;//未知异常
	return  a + b;
}
int main()
{
	//sub抛出异常不会再执行Division
	try { cout << sub(1, 3) << endl; Division(1, 0);}
	catch (const Exception& e) {cout << e.getErrMessage() << endl; }
	// 放到最后,捕获任意类型的异常,防止有一些异常没捕获,导致程序终止
	catch (...) { cout << "未知异常" << endl; }
	cout << "end" << endl;
	return 0;
}

7.有时候try catch无法一次性处理异常,或者因为抛出异常所造成的其他问题,所以catch处理完异常的一部分问题可以直接使用throw将异常重新抛出

#include<iostream>
using namespace std;
double Division(int a, int b)
{
	// 当b == 0时抛出异常
	if (b == 0){throw "Division by zero condition!";}
	return (double)a / (double)b;
}
void Func()
{
	int* array = new int[10];
	//捕获Division的异常,如果成功则将直接跳转到main函数,会造成array的内存泄漏
	try {cout << Division(0,0) << endl;}
	//先阶段捕获异常处理内存泄漏问题,在将异常重新抛出.
	catch (...){delete[] array;throw;}
	cout << "delete []" << array << endl;
	delete[] array;
}
int main()
{
	try{Func();}
	catch (const char* errmsg){cout << errmsg << endl;}
	return 0;
}

8.实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获,抛出和捕获的对象之间构成多态,这样可以知道错误处在哪里,也规范了抛出异常的类型,实际应用过程中,通公司都会自定义异常体系结构.

#include<iostream>
#include<thread>
using namespace std;
//下面是服务器常用异常体系
//基类异常
class Exception
{
public:
	Exception(int errid, const string& msg)
		:_errid(errid), _errmsg(msg){}
	virtual string what() const{return _errmsg;}
	int GetErrid() const{return _errid;}
protected:
	int _errid;     // 错误码
	string _errmsg; // 错误描述
};
//数据库异常
class SqlException : public Exception
{
public:
	SqlException(int errid, const string& msg, const string& sql)
		:Exception(errid, msg), _sql(sql){}
	//重写基类的错误信息获取函数
	virtual string what() const{return string("SqlException:" +_errmsg+"->"+_sql);}
protected:
	string _sql;
};

class CacheException : public Exception
{
public:
	CacheException(const string& errmsg, int id)
		:Exception(id, errmsg){}
	//重写基类的错误信息获取函数
	virtual string what() const{return string ("CacheException:"+_errmsg);}
};
class HttpServerException : public Exception
{
public:
	HttpServerException(const string& errmsg, int id, const string& type)
		:Exception(id, errmsg), _type(type){}//继承构造调用基类的构造函数构造基类的成员
	//重写基类的错误信息获取函数
	virtual string what() const{return string("HttpServerException:" + _errmsg+"->"+_type);}
private:
	const string _type;
};
void SQLMgr()
{
	srand(time(0));//抛出派生列
	if (rand() % 7 == 0){throw SqlException(100, "权限不足", "select * from name = '张三'");}
	cout << "调用成功" << endl;
}

void CacheMgr()
{
	srand(time(0));//抛出派生列
	if (rand() % 5 == 0){throw CacheException("权限不足", 100);}
	else if (rand() % 6 == 0){throw CacheException("数据不存在", 101);}
	SQLMgr();
}

void HttpServer()
{
	// 模拟服务出错
	srand(time(0));//抛出派生列
	if (rand() % 3 == 0){throw HttpServerException("请求资源不存在", 100, "get");}
	else if (rand() % 4 == 0){throw HttpServerException("权限不足", 101, "post");}
	CacheMgr();
}
int main()
{
	while (1)
	{
		this_thread::sleep_for(chrono::seconds(1));
		try{HttpServer();}
		catch (const Exception& e){cout << e.what() << endl;}//捕获父类
		catch (...){cout << "Unkown Exception" << endl;}
	}
	return 0;
}

9.c++标准库的异常体系
在这里插入图片描述
10.异常规范

// 这里表示这个函数会抛出A/B/C/D中的某种类型的异常
void fun() throw(A,B,C,D);
// 这里表示这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 这里表示这个函数不会抛出异常
void* operator delete (std::size_t size, void* ptr) throw();
// C++11 中新增的noexcept,表示不会抛异常,没有后缀表示可能会抛出异常
thread() noexcept;
thread (thread&& x) noexcept;

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值