[C++]异常处理,你不会不清楚吧
Posted @书生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[C++]异常处理,你不会不清楚吧相关的知识,希望对你有一定的参考价值。
异常概念:
异常是C++引入的一种新的处理错误方式,当函数发现一个自己无法处理的错误的时候,就会抛出异常,交给函数调用者进行处理
C语言中处理错误的方式:
1.使用断言assert() 进行判断
2.返回错误码
C++异常:
C++中引入了三个关键字throw ,catch, try 来支持异常
throw: 当出现错误,进行抛出的时候,是使用throw完成的
catch: 在可能出现错误的地方,对异常进行捕捉,是使用catch完成的
try: try块中存放着可能出现异常的代码
示例:
int Sub(int a,int b)
{
if (b == 0)
{
//除数为0,这种情况是不允许出现的,如果出现这样情况,就抛出异常
throw 0;
}
return a / b;
}
int main() {
try {
//Sub函数可能会抛出异常,需要放入try块中
int ret = Sub(7, 0);
cout << ret << endl;
}
catch (int err)
{
cout << err << endl;
}
return 0;
}
异常的某些规则
1.异常是通过抛出对象引发的,捕获的异常有哪部分代码处理,取决于catch的数据类型
int Sub(int a,int b)
{
if (b == 0)
{
throw 0;
}
return a / b;
}
int main() {
try {
int ret = Sub(7, 0);
cout << ret << endl;
}
catch (int err)
{
cout <<"int"<<err<< endl;
}
catch (char err)
{
cout << "char" <<err<< endl;
}
return 0;
}
抛出int类型的对象,就会进入 catch int 类型的代码块
2.异常和捕捉的数据类型匹配存在一个例外,可以抛出派生类,通过基类进行捕捉
class Exception{
public:
Exception(string str)
:_str(str)
{}
virtual void what() = 0 ;
protected:
string _str;
};
class SubErr : public Exception
{
public:
SubErr()
:Exception("除数为0")
{}
void what()
{
cout << _str << endl;
}
};
int Sub(int a,int b)
{
if (b == 0)
{
throw SubErr();
}
return a / b;
}
int main() {
try {
int ret = Sub(7, 0);
cout << ret << endl;
}
catch (Exception& err)
{
err.what();
}
return 0;
}
3.抛出异常对象时,会生成一个异常对象的拷贝;
class Exception{
public:
Exception(string str)
:_str(str)
{}
virtual void what() = 0 ;
void Printf()
{
cout << this << endl;
}
protected:
string _str;
};
class SubErr : public Exception
{
public:
SubErr()
:Exception("除数为0")
{}
void what()
{
cout << _str << endl;
}
};
int Sub(int a,int b)
{
if (b == 0)
{
SubErr se;
se.Printf();
throw se;
}
return a / b;
}
int main() {
try {
int ret = Sub(7, 0);
cout << ret << endl;
}
catch (Exception& err)
{
err.Printf();
err.what();
}
return 0;
}
4.catch(…)可以捕捉任何类型的对象,但是存在缺陷,无法知道异常对象的信息;
主要用于防止异常未被捕捉,造成程序崩溃的情况
5.异常被捕捉的位置一定时里异常抛出最近的位置
void Fun3()
{
try {
int ret = Sub(7, 0);
cout << ret << endl;
}
catch (Exception& err)
{
cout << "Fun3()" << endl;
err.what();
}
}
void Fun2()
{
try {
Fun3();
}
catch (int err)
{
cout << "Fun2()" << " int " << err << endl;
}
}
void Fun1()
{
try {
Fun2();
}
catch (Exception& err)
{
cout << "Fun1()" << endl;
err.what();
}
}
int main() {
try {
Fun3();
}
catch (...)
{
cout << "main" << endl;
}
return 0;
}
上面的结果时Fun3();如果将Fun3()中的捕捉类型改为char那么打印的结果是Fun1(),大家可以使用代码自己去尝试一下;
由此我们也可以了解到异常的匹配原则:
先检查throw是否是在try块中,如果是的话,查找匹配的catch块;在某个函数中没有找到匹配的catch块,则退出函数栈,进入该函数的调用者去查找匹配的catch块,如果main函数中都没有匹配的catch块,则程序强行终止
异常可以重新抛出
在某些情况下,单独的一个catch块可能不足以解决所有问题,我们需要异常的重新抛出
void Fun1(int* a,int* b)
{
try {
int ret = Sub(*a, *b);
cout << ret << endl;
}
catch (Exception& err)
{
err.what();
throw "除数为0";
}
}
int main() {
int* a = new int(7);
int* b = new int(0);
try {
Fun1(a,b);
}
catch (...)
{
delete a;
delete b;
cout << "main" << endl;
}
return 0;
}
如果在Fun1()中不重新抛出异常的话,就会造成资源泄露的问题。
异常规范
我们可以在可能出现错误的地方,抛出任何异常;但不好的是,调用者和函数编写者不是同一个人,这就造成了函数抛出异常,但是函数调用者不知道该捕捉什么类型,当然可以使用…进行捕捉,但是这样就无法处理异常;因此C++提出了异常规范;你可以在函数后面加上 throw(函数中可能抛出的异常类型) 告诉调用者该捕捉什么类型;
如果使用throw() 表示该函数不会抛出任何异常;
如果没有说明,则可以抛出任何类型的异常
如果编译器支持异常规范的时候,当函数内抛出的异常类型和生命的类型不相同时,就会编译失败
int Sub(int a,int b) throw(SubErr)//相当于做出承诺,指挥抛出SubErr类型的异常
{
if (b == 0)
{
SubErr se;
throw se;
}
return a / b;
}
int main() {
try {
int ret = Sub(7, 0);
cout << ret << endl;
}
catch (SubErr& err)
{
err.what();
}
return 0;
}
在C++11中有引用了一个新的关键字noexcept
异常安全:
1.在构造函数和析构函数当中不要抛出异常,如果抛出异常的话,可能会导致初始化或者资源没有被完全释放掉;
2.异常会导致资源泄漏的问题,我们需要使用对象管理资源来防止内存泄漏
C++库的异常体系
其实根据C++库的异常体系,我们也可以自己继承exception或者设计新的基类来实现自己的自定义异常体系
异常的优缺点:
优点:
1.相比于错误码可以更好的展示错误信息,甚至可以包含堆栈的调用信息,可以更好的定位BUG
2.返回错误码的最大弊端是 深层函数发生错误,需要进行层层返回,到最外层(main)才能拿到错误码
3.部分函数使用异常更加好进行处理,比如有些成员函数是不能通过返回值的方法来返回错误码的
缺点:
1.异常时会导致执行流进行跳转的,我们在调试程序和分析程序的时候,可能会非常困难。
2.异常其实是有一定的性能损耗的。(由于现在的硬件性能,当然也可以忽略不计)
3.如果使用者不遵循异常规范,对于调用者来说,是很不方便的。(不知道应该捕捉什么异常)
4.C++标准库库中的异常体系有所欠缺
5.C++时没有垃圾回收机制的,使用异常有可能会造成内存泄漏。
注:如果本篇博客有任何错误和建议,欢迎伙伴们留言,你快说句话啊!
以上是关于[C++]异常处理,你不会不清楚吧的主要内容,如果未能解决你的问题,请参考以下文章