C++&Qt的异常处理

引言

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

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

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

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

异常处理的方法

例1

假设我们写一个程序,把用户输入的两个字符串转换为整数,相加输出

#include 
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 
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 
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   
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<"/"<"="<cout<"/"<"="<catch(int)//异常类型  
    {  
        cout<<"除数为0,计算错误!"<//异常处理语句  
    }  
    catch(double)//异常类型  
    {  
        cout<<"除数为0.0,计算错误!"<//异常处理语句  
    }  
    return 0;  
}  

例3

求三角形周长

#include   
#include   
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<cout<return 0;  
}  

例4

在异常处理中处理析构函数

#include   
#include   
using namespace std;  
class Student  
{  
public:  
    Student(int n,string nam):num(n), name(nam)  
    {  
        cout<<"constructor-"<cout<<"destructor-"<void get_data( );  
private:  
    int num;  
    string name;  
};  
void Student::get_data( )  
{  
    if(num==0)  
        throw num; //如num=0,抛出int型变量num  
    else  
        cout<" "<cout<" is in get_data()!"<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="<",error!"<return 0;  
}  

例5

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

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

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

你可能感兴趣的:(C/C++)