C++&Qt的异常处理未完待续

##引言
我们寄希望使用异常这种方法,让一个函数发现自己无法处理的错误时抛出异常或者做进一步处理。未使用异常处理机制的程序,当遇见无法处理的问题时可能会产生如下后果:

  1. 程序自行终止(然后程序员开始漫长的找bug过程)
  2. 返回一个表示错误的值(很多系统函数都是这样,例如malloc,内存不足,分配失败,返回NULL指针)
  3. 返回一个合法值,让程序处于某种非法的状态(最坑爹的东西,有些第三方库真会这样)
  4. 调用一个预先准备好在出现"错误"的情况下用的函数。

第一种情况在软件开发过程中是不明智的,当软件体量的增大,找bug的过程往往将浪费掉大量时间。第二种情况,比较常用,但是有时不合适,例如返回错误码是int,每个调用都要检查错误值,极不方便,也容易让程序规模加倍。第三种情况,很容易误导调用者。

通过使用异常,把错误和相应处理分开。由函数抛出异常,调用者可以根据捕获异常判断程序出错原因和位置,做出相应处理。是否终止程序由调用者掌握,而不是任由程序自行宕机。

##异常处理的方法
###例1
假设我们写一个程序,把用户输入的两个字符串转换为整数,相加输出

#include <iostream>
void main()
{
	char *str1 = "1", *str2 = "2";
	int num1 = atoi(str1);
	int num2 = atoi(str2);
	std::cout << "sum is "<< num1 + num2 << std::endl;
}

假设用户输入的是str1,str2,如果str1和str2都是整数类型的字符串,这段代码是可以正常工作的,但是用户的输入有可能误操作,输入了非法字符,例如

#include <iostream>
void main()
{
	char *str1 = "1", *str2 = "a";
	int num1 = atoi(str1);
	int num2 = atoi(str2);
	std::cout << "sum is "<< num1 + num2 << std::endl;
}

这个时候结果是1,因为atoi(str2)返回0。如果用户输入是这样:

#include <iostream>
void main()
{
	char *str1 = "1", *str2 = NULL;
	int num1 = atoi(str1);
	int num2 = atoi(str2);
	std::cout << "sum is "<< num1 + num2 << std::endl;
}

那么这段代码会出现段错误,程序异常退出。
如果在一个重要系统中,调用者不知情,传入了一个NULL字符,程序就异常退出了,导致服务中断,或者传入非法字符,结果返回0。为了解决这种问题,异常处理改造一个安全的atoi方法,叫parseNumber。


class NumberParseException {};

bool isNumber(char * str) {
	using namespace std;
	if (str == NULL)
		return false;
	int len = strlen(str);
	if (len == 0)
		return false;
	bool isaNumber = false;
	char ch;
	for (int i = 0; i < len; i++) {
		if (i == 0 && (str[i] == '-' || str[i] == '+'))
			continue;
		if (isdigit(str[i])) {
			isaNumber = true;
		}
		else {
			isaNumber = false;
			break;
		}
	}
	return isaNumber;
}

int parseNumber(char * str) throw(NumberParseException) {
	if (!isNumber(str))
		throw NumberParseException();
	return atoi(str);
}

void main()
{
	char *str1 = "1", *str2 = NULL;
	try {
		int num1 = parseNumber(str1);
		int num2 = parseNumber(str2);
		std::cout << "sum is " << num1 + num2 << std::endl;
	}
	catch (NumberParseException) {
		std::cout << "输入不是整数\n";
	}
}

上述代码中NumberParseException是自定义的异常类,当检测的时候传入的str不是一个数字时,就抛出一个数字转换异常,让调用者处理错误,这比传入NULL字符串,导致段错误结束程序好得多,调用者可以捕获这个异常,决定是否结束程序,也比传入一个非整数字符串,返回0要好,程序出现错误,却继续无声无息执行下去。

###例2
防止除数为0

#include <iostream>  
using namespace std;  
template <typename T>  
T Div(T x,T y)  
{  
    if(y==0)  
        throw y;//抛出异常  
    return x/y;  
}  
  
  
int main()  
{  
    int x=5,y=0;  
    double x1=5.5,y1=0.0;  
    try  
    {  
        //被检查的语句  
        cout<<x<<"/"<<y<<"="<<Div(x,y)<<endl;  
        cout<<x1<<"/"<<y1<<"="<<Div(x1,y1)<<endl;  
    }  
    catch(int)//异常类型  
    {  
        cout<<"除数为0,计算错误!"<<endl;//异常处理语句  
    }  
    catch(double)//异常类型  
    {  
        cout<<"除数为0.0,计算错误!"<<endl;//异常处理语句  
    }  
    return 0;  
}  

###例3
求三角形周长

#include <iostream>  
#include <stdexcept>  
using namespace std;  
int triangle(int a, int b, int c)  
{  
    if(a<0 || b<0 || c<0 || a+b<=c || a+c<=b || b+c<=a)  
        throw runtime_error("The lengths of three sides can't form triangle");  
    return a + b + c;  
}  
  
  
int main()  
{  
    int total = 0;  
    try  
    {  
        total = triangle(3,4,7);  
    }  
    catch(const runtime_error& e)  
    {  
        cout<<e.what()<<endl;  
    }  
    cout<<total<<endl;  
    return 0;  
}  

###例4
在异常处理中处理析构函数

#include <iostream>  
#include <string>  
using namespace std;  
class Student  
{  
public:  
    Student(int n,string nam):num(n), name(nam)  
    {  
        cout<<"constructor-"<<n<<endl;  
    }  
    ~Student( )  
    {  
        cout<<"destructor-"<<num<<endl;  
    }  
    void get_data( );  
private:  
    int num;  
    string name;  
};  
void Student::get_data( )  
{  
    if(num==0)  
        throw num; //如num=0,抛出int型变量num  
    else  
        cout<<num<<" "<<name<<endl;  
    cout<<num<<" is in get_data()!"<<endl;  
}  
  
  
void fun( )  
{  
    Student stud1(1101,"Tan");  
    stud1.get_data( );  
    Student stud2(0,"Li");  
    stud2.get_data( );  
}  
  
  
int main( )  
{  
    try  
    {  
        fun( );  
    }  
    catch(int n)  
    {  
        cout<<"num="<<n<<",error!"<<endl;  
    }  
    return 0;  
}  

###例5
在函数嵌套的情况下检测异常处理

#include <iostream>  
using namespace std;  
int main( )  
{  
    void f1( );  
    try  
    {  
        f1( );   //调用f1( )  
    }  
    catch(double)  
    {  
        cout<<"OK0!"<<endl;  
    }  
    cout<<"end0"<<endl;  
    return 0;  
}  
void f1( )  
{  
    void f2( );  
    try  
    {  
        f2( );   //调用f2( )  
    }  
    catch(char)  
    {  
        cout<<"OK1!";  
    }  
    cout<<"end1"<<endl;  
}  
void f2( )  
{  
    void f3( );  
    try  
    {  
        f3( );   //调用f3( )  
    }  
    catch(int)  
    {  
        cout<<"Ok2!"<<endl;  
    }  
    cout<<"end2"<<endl;  
}  
void f3( )  
{  
    double a=0;  
    try  
    {  
        throw a;   //抛出double类型异常信息  
    }  
    catch(float)  
    {  
        cout<<"OK3!"<<endl;  
    }  
    cout<<"end3"<<endl;  
}  

参考
https://www.cnblogs.com/ggjucheng/archive/2011/12/18/2292089.html
http://blog.csdn.net/sxhelijian/article/details/46128213

  • 5
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值