C++--第26课 - 异常处理 - 下

第26课 - 异常处理 - 下

1. 问题一

有时在工程中关心是否产生了异常,而不关心具体的异常类型,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)  //只能放在catch之前,否则成了死代码

        {  

            cout<<"Exception: "<<e<<endl;

        }

        catch(...)

        {

            cout<<"Exception Occur"<<endl;

        }

    }

    cout << "Press the enter key to continue ...";

    cin.get();

    return EXIT_SUCCESS;

}

运行结果:

0

Exceptin Occur

Exceptin Occur

Exceptin Occur

Exceptin:c

5

6

7

8

9

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

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

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

 

2. 问题二

在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;   //有异常,终止了for循环

            }

        }

    }

    catch(int e)

    {

        cout<<"Catch: "<<e<<endl;

    }

    cout << "Press the enter key to continue ...";

    cin.get();

    return EXIT_SUCCESS;

}

运行结果:

0

1

2

3

4

5

Exception:6

Catch:6

 

3. 问题三

在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;

            }

        }

    }

    catch(int e)

    {

        cout<<"Catch: "<<e<<endl;

    }

    cout << "Press the enter key to continue ...";

    cin.get();

    return EXIT_SUCCESS;

}

运行结果:

0

1

2

3

4

5

Exception Occur

Catch:6

不要在构造函数中抛出异常。在构造函数可能申请系统资源,而在构造函数中抛出异常会导致对象构造不完全。不完全对象的析构函数是不会被调用的,因此可能造成资源泄漏。

 

4. 问题4

不要在构造函数中抛出异常。在构造函数可能申请系统资源,而在构造函数中抛出异常会导致对象构造不完全。不完全对象的析构函数是不会被调用的,因此可能造成资源泄漏。语法上来说是合法的,但是会造成问题。

构造函数中的异常示例。

#include <cstdlib>

#include <iostream>

using namespace std;

class Test

{

    int* p;

public:

    Test()

    {

        cout<<"Test()"<<endl;

        p = new int[5];

        throw 10; /*构造函数里面抛出异常,在这里没有被处理,去上一层main函数中 */

    }

    ~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;

}

运行结果:

Test()

Catch:10

 

5. 工程中的异常应用

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

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

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

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

 

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

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

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

exception类有两个主要的分支:logic_error用于描述程序中出现的逻辑错误,如—传递无效参数;runtime_error用于描述无法预料的事件所造成的错误,如—内存耗尽,硬件错误。

 

标准库中的异常

 

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...");

    }

    return  a / b;

}

int main(int argc, char *argv[])

{

    try

    {

        cout<<Div(1, 0)<<endl;

    }

    catch(exception& e)

    {

        cout<<e.what()<<endl;

    }

    cout << "Press the enter key to continue ...";

    cin.get();

    return EXIT_SUCCESS;

}

运行结果:

Divide by zero...

 

6. 函数级try语法

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

int func(int i)

{

try

{

return i;

}

catch(...)

{

return -1;

}

}

等价于

int func(int i) try

{

return i;

catch(...)

{

return -1;

}

}

 

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

#include <cstdlib>

#include <iostream>

#include <stdexcept>

using namespace std;

int func1(int i)

{

    try

    {

        if( i > 0 )

        {

            return i;

        }

        else

        {

            throw "error";

        }

    }

    catch(...)

    {

        return -1;

    }

}

int func2(int i) 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;

}

运行结果:

-1

1

2

3

4

 

小结:

catch(...)可以捕获所有异常。

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

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

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

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

 

posted @ 2019-08-11 19:32  free-锻炼身体  阅读(144)  评论(0编辑  收藏  举报