异常处理(下)

深入异常处理

问题:有时在工程中只关心是否产生 了异常,而不关心具体的异常类型,C++语言可以做到吗?

C++中的 catch语句可以使用…捕获所有的异常

#include <cstdlib>
#include <iostream>

using namespace std;

int test(int i)
{
    if( i == 1 )
    {
        throw "p";
    }
    
    if( i == 2 )
    {
        throw 0.5;
    }
    
    if( i == 3 )
    {
        throw 3;
    }
    
    if( i == 4 )
    {
        throw 'c';
    }
    
    return i;
}

int main(int argc, char *argv[])
{
    for(int i=0; i<10; i++)
    {
        try
        {
            cout<<test(i)<<endl;
        }
        catch(char e)
        {   
            cout<<"Exception: "<<e<<endl;
        }
        catch(...) //...捕获所有异常 
        {
            cout<<"Exception Occur"<<endl;
        }
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bQNVDR3v-1583218660626)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583118520159.png)]

catch(…) 可以捕获所有异常但却无法得到异常信息

catch(…) 一般作为最后一个异常处理块出现

tips:

看见代码中的catch就要意识到这里在处理异常情况,而异常是在对应的try中产生的。

在catch语句块中仍然可以抛出异常

#include <cstdlib>
#include <iostream>

using namespace std;

int test(int i)
{
    if( (6 <= i) && (i <= 9) )
    {
        throw i;
    }
    
    return i;
}

int main(int argc, char *argv[])
{
    try
    {
        for(int i=0; i<10; i++)
        {
            try
            {
                cout<<test(i)<<endl;
            }
            catch(int e)
            {   
                cout<<"Exception: "<<e<<endl;
                
                throw e;
            }
        }
    }
    catch(int e)
    {
        cout<<"Catch: "<<e<<endl;
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hdTiddZW-1583218660628)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583119585969.png)]

在catch(…) 语句块中,可以通过不带参数的 throw语 句抛出捕获的异常

#include <cstdlib>
#include <iostream>

using namespace std;

int test(int i)
{
    if( (6 <= i) && (i <= 9) )
    {
        throw i;
    }
    
    return i;
}

int main(int argc, char *argv[])
{
    try
    {
        for(int i=0; i<10; i++)
        {
            try
            {
                cout<<test(i)<<endl;
            }
            catch(...)
            {   
                cout<<"Exception Occur"<<endl;
                
                throw; //throw可以不带参数 
            }
        }
    }
    catch(int e)
    {
        cout<<"Catch: "<<e<<endl;
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zi4xbb3w-1583218660629)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583120054054.png)]

异常与对象

不要在构造函数中抛出异常

​ 在构造函数可能申请系统资源,而在构造函数中抛出异常会导致对象构造不完全

​ 不完全对象的析构函数是不会被调用的,因此可能造成资源泄漏

构造函数中的异常示例

#include <cstdlib>
#include <iostream>

using namespace std;

class Test
{
    int* p;
public:
    Test()
    {
        cout<<"Test()"<<endl;
        
        p = new int[5];
        
        throw 10; //构造函数中抛出异常
    }
    
    ~Test()
    {
        cout<<"~Test()"<<endl;
        delete[] p;
    }
};

int main(int argc, char *argv[])
{
    try
    {
        Test t;
    }
    catch(int e)
    {
        cout<<"Catch: "<<e<<endl;
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t8vj1A23-1583218660630)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583138795377.png)]

分析:析构函数没有被调用

工程中的异常应用

在工程中会定义一系列的异常类

通过继承,可以得到一个异常类族

每个类代表工程中可能出现的一种异常类型

由于对象构造与拷贝的开销,在定义catch语句块时使用引用作为参数

在工程中可以使用标准库中的异常类

可将标准库中的异常类作为基类派生新的异常类

标准库中的异常都是从exception类派生的

exception类有两个主要的分支

​ logic_error用于描述程序中出现的逻辑错误

​ 如:传递无效参数

runtime_error用于描述无法预料的事件所造成的错误

如:内存耗尽 ,硬件错误

标准库中的异常

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a9qHTtxh-1583218660631)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583204196026.png)]

logic_error和runtime_error都提供了一个参数为字 符串的构造函数,这样就可以保持错误信息

通过what()成员函数就可以得到错误的信息

异常的工程应用初探

#include <cstdlib>
#include <iostream>
#include <stdexcept>

using namespace std;

class divide_by_zero : public logic_error{
public:
	divide_by_zero(const char* s)	 : logic_error(s) { //调用父类构造函数 
		
	}
};

double Div(double a, double b){
	if((-0.00000001 < b) && (b < 0.00000001) ){
		throw divide_by_zero("Divide by zero..."); //产生一个临时对象 
		//此处为什么要用重新继承的 divide_by_zero类哪? 因为 logic_error不太符合 
	}
	
	return a/b;
}

int main(int argc, char *argv[])
{
    try
    {
        cout << Div(2,0)<<endl;
    }
    catch(exception& e) //引用:避免拷贝 
    					//此处赋值兼容性原则 
    					//异常类族:可以捕获所有异常 
    {
        cout << e.what()<<endl;
    }
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QpmoFkfK-1583218660633)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583203974373.png)]

函数级try语法

可以将函数体作为一个完整的try语句块

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v0sBNohF-1583218660638)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583205544800.png)]

函数级try语法可以更好将正常逻辑代码与异 常处理代码分开,提高代码的可读性与维护 性

#include <cstdlib>
#include <iostream>
#include <stdexcept>

using namespace std;

int func(int i){
	try{
		if(i > 0){
			return i;
		}
		else{
			throw "error";
		}
	}
	catch(...){
		return -1;
	}
}

int func2(int i) try //函数级try语句块 
{
	if(i > 0){
		return i;
	}
	else{ 
		throw  "error";
	}	
}
catch(...) {
	return -1;
}

int main(int argc, char *argv[])
{
    for(int i=0; i<5; i++){
    	cout << func2(i) <<endl;
	}
    
    cout << "Press the enter key to continue ...";
    cin.get();
    return EXIT_SUCCESS;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ed0rgqlF-1583218660638)(E:\md文件资源%5CUsers%5CWGJ%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1583205159471.png)]

小结

catch(…) 可以捕获所有异常

catch(…) 经常作为最后一个 catch语句出现

不要在构造函数中抛出异常,这样可能造成资源泄露

工程中经常以标准库中的异常类作为项目异常的基础

函数级try语句块能够更好的提高代码的维护性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值