C++异常

C语言传统处理错误的方式
1.终止程序。这就很恶心。直接导致程序崩溃。
2.返回错误码:在出现一些错误时,会返回错误码,程序员需要到错误码表中查找对应错误信息。

C++异常

概念:当一个函数发现自己无法处理一个错误时,它就通过抛异常的方式将异常给另一个函数来处理。这里引出了throw,catch,try。
throw:将会出现异常的变量用throw关键字来修饰。
catch:将想要处理异常交给catch来处理,一个throw可以对应多个catch对应。
try:try 块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个 catch 块,try块中放着可能抛出异常的代码。格式如下

try{
        //可能会出现异常的代码
}
catch(ExceptionName e1){
}
catch(ExceptionName e2){
}
catch(ExceptionName eN){
}

异常的抛出和匹配原则
1.异常的捕获处理是根据抛出异常对象的类型来决定的。
2.选中被处理的代码是离抛出对象最近且匹配的。
3.抛出异常对象后,会生成一个异常对象的拷贝,因为有可能抛出的对象是一个临时对象,在出了作用域会被析构,当catch捕捉到后处理完后销毁。
4.抛出对象的捕获有个例外,并不是所有对象类型都匹配,如果异常对象为派生类,捕获对象类型可以使用基类类型。
5.catch(…)表示可以捕获任何类型的异常,但不知道异常错误是什么
在函数调用链中,异常栈匹配原则
1.首先看throw本身是否在try内部,如果是查找匹配的catch语句,如果有匹配的,则调到匹配的catch地方处理。
2.没有匹配的catch则退出当前函数调用栈,到其他函数调用栈找catch
3.如果到达了main函数栈还没有找到匹配的,则终止程序。沿着函数调用栈找catch的过程叫做栈展开,所以在所有catch的最后一个都要写catch(…),b不然程序就会被终止。
4.如果匹配到了catch,catch执行完后,就会执行catch后面的语句。

int  Division(int a,int b)
{
    if(b == 0)
      throw "Division by zero condition!";
    else{
        return a/b;
    }
}
void Func()
{
	int len, time;
	cin >> len >> time;
	cout << Division(len, time) << endl;
}
int main()
{
     try{
                 Func();
     }
     catch(const char *message)
     {
         cout<<message<<endl;
     }
     catch(...)
     {
        cout<<"unkown exception"<<endl;
     }
     return 0;
}

异常的重新抛出
可能单个catch不能完全处理一个异常,在进行一个校正处理后,希望再交给更外层的调用链来处理,catch则可以通过重新抛出异常交给更上层的函数处理。

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];
   try {
		int len, time;
		cin >> len >> time;
		cout << Division(len, time) << endl;
		}
	catch(...)
	{
	     delete[] array;
	     throw;
	}	
	delete[] array;
}
int main()
{
     try {
           Func();
     }
     catch(char* message)
     {
           cout<<message<<endl;
     }
}

在上面的代码中,Func函数内为array申请了内存,如果在进行Division函数时出现了b==0的情况,那么此时就会抛异常,找到了最近的catch(…)且匹配,此时释放掉了array空间,如果不进行再次抛异常,会接着执行catch(…)后的代码,后面是没有抛异常的情况,也对array空间进行释放,此时,array就被释放两次,就会发生错误。所以需要再次抛异常,然后到main函数栈内的 catch(char* message)内处理。
异常安全
1.构造函数内最好不要抛异常,可能会导致成员没有初始化完全。
2.析构函数同样也不要出现异常,可能会导致内存泄露。
3.在new和delete中抛出异常,可能会导致资源泄露的问题,在lock和unlock中出现异常,可能会导致死锁。所以C++出现了RALL思想,用只能指针能很好解决异常安全问题。
异常规范
1.在函数后面通常接throw(类型)表明函数只抛的异常类型。
2.在函数后面接throw(),表示函数不抛任何类型的异常。
3.若函数后面没有任何东西,则表明可以抛任何类型的异常。

// 这里表示这个函数会抛出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 new (std::size_t size, void* ptr) throw();

异常优缺点

异常优点
1.异常定义好了,相比错误码的方式,异常更能直接的看到错误信息。
2.若使用错误码,有时候深层次的函数,需要层层返回才能得到错误码再得到错误信息,异常处理则会直接被catch得到错误信息。
3.部分没有返回值的函数使用异常更友好,比如T&operator这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误。
异常缺点
1.异常会打断程序的执行流,在运行时就很恶心,也不方便调试。
2.异常通常会导致内存泄露,死锁等问题。
3.C++对异常体系库提供的不是很好,就需要自己去定,但是很混乱。
4.异常使用得尽量规范,否则会随意抛异常,很恶心。
5.异常还会有些性能上消耗,不过对于现在CPU可以忽略不计。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值