• Cpp Chapter 15: Friends, Exceptions, and More Part2


    // 这个part因为网络问题被吞了一部分

    15.3 Exceptions

    ) Calling abort()
    When you already know the conditions of failure, you may detect them and call abort() if true to terminate the program directly, without even returning to main():

    // error1.cpp -- using the abort() function
    #include <iostream>
    #include <cstdlib>
    double hmean(double a, double b);
    
    int main()
    {
        double x, y, z;
        std::cout << "Enter two numbers: ";
        while (std::cin >> x >> y)
        {
            z = hmean(x, y);
            std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl;
            std::cout << "Enter next set of numbers <q to quit>: ";
        }
        std::cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
        {
            std::cout << "untenable arguments to hmean()
    ";
            std::abort();
        }
        return 2.0 * a * b / (a + b);
    }
    

    ) Returning an error code
    A program could also use a function's return value to indicat a problem. In fact, every numeric value could be a valid return type:

    #include <iostream>
    #include <cstdlib>
    double hmean(double a, double b);
    
    int main()
    {
        double x, y, z;
        std::cout << "Enter two numbers: ";
        while (std::cin >> x >> y)
        {
            z = hmean(x, y);
            std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl;
            std::cout << "Enter next set of numbers <q to quit>: ";
        }
        std::cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
        {
            std::cout << "untenable arguments to hmean()
    ";
            std::abort();
        }
        return 2.0 * a * b / (a + b);
    }
    

    ) The exception mechanism
    1 Throwing an exception : by using keyword throw followed with a string or an object, tells a program to jump to somewhere else.
    2 Catching an exception with handler: by using keyword catch followed with a block of code, which means that if specific error is encountered, then code within the catch block would be executed.
    3 Using a try block: identifies a block of code which might activate certain errors, usually followed by several catch blocks
    Here comes an example illustrating the mechanism of exceptions:

    #include <iostream>
    #include <cstdlib>
    double hmean(double a, double b);
    
    int main()
    {
        double x, y, z;
        std::cout << "Enter two numbers: ";
        while (std::cin >> x >> y)
        {
            z = hmean(x, y);
            std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl;
            std::cout << "Enter next set of numbers <q to quit>: ";
        }
        std::cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
        {
            std::cout << "untenable arguments to hmean()
    ";
            std::abort();
        }
        return 2.0 * a * b / (a + b);
    }
    

    Noteworthy:
    1 If any statement in the try block leads to an exception being thrown, the catch block after this will handle the exception. If there are multiple catch blocks after a try block, the compiler will search for the correct handler with the correct type which corresponds to what has been thrown.
    2 If a program executes code in try block without any exceptions being thrown, it skips the catch blocks after it and jumps to the very next line of code.

    ) Using objects as exceptions
    In the previous example, the throw follows a string. But most of time you want it to throw an object, because objects could store information and indicate which kind of error it is specifically. Here comes an example implementing the function of calculating harmonic mean and geometric mean of two numbers, also providing two type of exceptions:

    // exc_mean.h -- exception classes for hmean(), gmean()
    #include <iostream>
    #include <cmath>
    
    class bad_hmean
    {
    private:
        double v1;
        double v2;
    public:
        bad_hmean(double a = 0, double b = 0) : v1(a), v2(b) {}
        void mesg();
    };
    
    inline void bad_hmean::mesg()
    {
        std::cout << "hmean(" << v1 <<", " << v2 << "): " << "invalid arguments: a = -b
    ";
    }
    
    class bad_gmean
    {
    public:
        double v1;
        double v2;
        bad_gmean(double a = 0, double b = 0) : v1(a), v2(b) {}
        const char * mesg();
    };
    
    inline const char * bad_gmean::mesg()
    {
        return "gmean() arguments should be >= 0
    ";
    }
    
    double hmean(double a, double b);
    double gmean(double a, double b);
    int main()
    {
        using std::cout;
        using std::cin;
        using std::endl;
    
        double x, y, z;
        cout << "Enter two numbers: ";
        while (cin >> x >> y)
        {
            try
            {
                z = hmean(x, y);
                cout << "Harmonic mean of " << x << " and " << y << " is " << z << endl;
                cout << "Geometric mean of " << x << " and " << y << " is " << gmean(x,y) << endl;
                cout << "Enter next set of numbers <q to quit>: ";
            }
            catch (bad_hmean & bg)
            {
                bg.mesg();
                cout << "Try again.
    ";
                continue;
            }
            catch (bad_gmean & hg)
            {
                cout << hg.mesg();
                cout << "Values used: " << hg.v1 << ", " << hg.v2 << endl;
                cout << "Sorry, you don't get to plat any more.
    ";
                break;
            }
        }
        cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
            throw bad_hmean(a,b);
        return 2.0 * a * b / (a + b);
    }
    
    double gmean(double a, double b)
    {
        if (a < 0 || b < 0)
            throw bad_gmean(a,b);
        return std::sqrt(a * b);
    }
    

    ) Unwinding the stack
    In C++, a function normally returns to the function that calls it, with each function liberating its automatic variables as it terminates. If an automatic variable is an object, the destructor for that object is automatically called.
    Suppose you now terminate a function via throw. After a function was terminated by throw, it ends immediately and pass back control to main().While freeing memory on the stack, instead of stopping at the first return address on the stack, the program continues freeing the stack until it reaches a return address which resides in the try block. This is to say, although a function is terminated by throw, the program would free all intermediate functions' automatic variables on the stack, otherwise the intermediate function's variables would be left unfreed.
    This technique is called unwinding the stack, which is extremely important to exceptions.
    Here comes the code illustrating unwinding the stack:

    // error5.cpp -- unwinding the stack
    #include <iostream>
    #include <cmath>
    #include <string>
    #include "exc_mean.h"
    
    class demo
    {
    private:
        std::string word;
    public:
        demo(const std::string & str)
        {
            word = str;
            std::cout << "demo " << word <<" created
    ";
        }
        ~demo()
        {
            std::cout << "demo " << word << " destroyed
    ";
        }
        void show() const
        {
            std::cout << "demo " << word << " lives!
    ";
        }
    };
    
    double hmean(double a, double b);
    double gmean(double a, double b);
    double means(double a, double b);
    
    int main()
    {
        using std::cout;
        using std::cin;
        using std::endl;
    
        double x, y, z;
        {
            demo d1("found in block in main()");
            cout << "Enter two numbers: ";
            while (cin >> x >> y)
            {
                try
                {
                    z = means(x,y);
                    cout << "The mean mean of " << x << " and " << y << " is " << z << endl;
                    cout << "Enter next pair: ";
                }
                catch (bad_hmean & bg)
                {
                    bg.mesg();
                    cout << "Try again.
    ";
                    continue;
                }
                catch (bad_gmean & hg)
                {
                    cout << hg.mesg();
                    cout << "Values used: " << hg.v1 << ", " << hg.v2 << endl;
                    cout << "Sorry, you don't get to play any more.
    ";
                    break;
                }
            }
            d1.show();
        }
        cout << "Bye!
    ";
        cin.get();
        cin.get();
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
            throw bad_hmean(a,b);
        return 2.0 * a * b / (a + b);
    }
    
    double gmean(double a, double b)
    {
        if (a < 0 || b < 0)
            throw bad_gmean(a,b);
        return std::sqrt(a * b);
    }
    
    double means(double a, double b)
    {
        double am, hm, gm;
        demo d2("found in means()");
        am = (a + b) / 2.0;
        try
        {
            hm = hmean(a,b);
            gm = gmean(a,b);
        }
        catch (bad_hmean & bg)
        {
            bg.mesg();
            std::cout << "Caught in means()
    ";
            throw; // rethrow the exception
        }
        d2.show();
        return (am + hm + gm) / 3.0;
    }
    

    ) More exception features
    In previous code, while throwing and exception, you used this code:

    try
    {
        super();
    }
    catch (problem & p)
    {
        ...
    }
    ...
    void super()
    {
        ...
        throw problem(); //notice!
    }
    

    You used throw problem() here because the compiler would always generate a temporary copy while throwing exceptions. Another reason is that you could use base-class reference to point to derived-class objects, so a reference could handle all exception objects in the hierarchy. Meanwhile, the order of catch blocks should also be considered, you shoule put derived-class catch blocks in advance of base-class catch blocks, because the program first reaches the derived-class catch block, and it the type matches, it will run the code and ignore the remaining, empowering you to treat exception objects of different classes separately.

    )The exception class
    C++ incorporates exception class to support exception features. Generally, logic_error and runtime_error classes derives from exception, which separately contains some sort of exception classes.
    The logic_error class contains:
    1 domain_error class, just as functions have domain and range, if you violate that you could throw a domain_error object.
    2 invalid_argument class, when your function receives data which is not desirable or not fit the rules, you could throw a invalid_argument object.
    3 length_error class, indicating that not enough space is available for the desired action.
    4 out_of_bounds class, indicating indexing errors, such as array index out of range.
    Each of these classes mentioned above have a constructor.
    You could also derive your own exception objects from logic_error or runtime_error classes.

    ---恢复内容结束---

    ##15.3 Exceptions
    ) Calling abort()
    When you already know the conditions of failure, you may detect them and call abort() if true to terminate the program directly, without even returning to main():

    // error1.cpp -- using the abort() function
    #include <iostream>
    #include <cstdlib>
    double hmean(double a, double b);
    
    int main()
    {
        double x, y, z;
        std::cout << "Enter two numbers: ";
        while (std::cin >> x >> y)
        {
            z = hmean(x, y);
            std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl;
            std::cout << "Enter next set of numbers <q to quit>: ";
        }
        std::cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
        {
            std::cout << "untenable arguments to hmean()
    ";
            std::abort();
        }
        return 2.0 * a * b / (a + b);
    }
    

    ) Returning an error code
    A program could also use a function's return value to indicat a problem. In fact, every numeric value could be a valid return type:

    #include <iostream>
    #include <cstdlib>
    double hmean(double a, double b);
    
    int main()
    {
        double x, y, z;
        std::cout << "Enter two numbers: ";
        while (std::cin >> x >> y)
        {
            z = hmean(x, y);
            std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl;
            std::cout << "Enter next set of numbers <q to quit>: ";
        }
        std::cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
        {
            std::cout << "untenable arguments to hmean()
    ";
            std::abort();
        }
        return 2.0 * a * b / (a + b);
    }
    

    ) The exception mechanism
    1 Throwing an exception : by using keyword throw followed with a string or an object, tells a program to jump to somewhere else.
    2 Catching an exception with handler: by using keyword catch followed with a block of code, which means that if specific error is encountered, then code within the catch block would be executed.
    3 Using a try block: identifies a block of code which might activate certain errors, usually followed by several catch blocks
    Here comes an example illustrating the mechanism of exceptions:

    #include <iostream>
    #include <cstdlib>
    double hmean(double a, double b);
    
    int main()
    {
        double x, y, z;
        std::cout << "Enter two numbers: ";
        while (std::cin >> x >> y)
        {
            z = hmean(x, y);
            std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl;
            std::cout << "Enter next set of numbers <q to quit>: ";
        }
        std::cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
        {
            std::cout << "untenable arguments to hmean()
    ";
            std::abort();
        }
        return 2.0 * a * b / (a + b);
    }
    

    Noteworthy:
    1 If any statement in the try block leads to an exception being thrown, the catch block after this will handle the exception. If there are multiple catch blocks after a try block, the compiler will search for the correct handler with the correct type which corresponds to what has been thrown.
    2 If a program executes code in try block without any exceptions being thrown, it skips the catch blocks after it and jumps to the very next line of code.

    ) Using objects as exceptions
    In the previous example, the throw follows a string. But most of time you want it to throw an object, because objects could store information and indicate which kind of error it is specifically. Here comes an example implementing the function of calculating harmonic mean and geometric mean of two numbers, also providing two type of exceptions:

    // exc_mean.h -- exception classes for hmean(), gmean()
    #include <iostream>
    #include <cmath>
    
    class bad_hmean
    {
    private:
        double v1;
        double v2;
    public:
        bad_hmean(double a = 0, double b = 0) : v1(a), v2(b) {}
        void mesg();
    };
    
    inline void bad_hmean::mesg()
    {
        std::cout << "hmean(" << v1 <<", " << v2 << "): " << "invalid arguments: a = -b
    ";
    }
    
    class bad_gmean
    {
    public:
        double v1;
        double v2;
        bad_gmean(double a = 0, double b = 0) : v1(a), v2(b) {}
        const char * mesg();
    };
    
    inline const char * bad_gmean::mesg()
    {
        return "gmean() arguments should be >= 0
    ";
    }
    
    double hmean(double a, double b);
    double gmean(double a, double b);
    int main()
    {
        using std::cout;
        using std::cin;
        using std::endl;
    
        double x, y, z;
        cout << "Enter two numbers: ";
        while (cin >> x >> y)
        {
            try
            {
                z = hmean(x, y);
                cout << "Harmonic mean of " << x << " and " << y << " is " << z << endl;
                cout << "Geometric mean of " << x << " and " << y << " is " << gmean(x,y) << endl;
                cout << "Enter next set of numbers <q to quit>: ";
            }
            catch (bad_hmean & bg)
            {
                bg.mesg();
                cout << "Try again.
    ";
                continue;
            }
            catch (bad_gmean & hg)
            {
                cout << hg.mesg();
                cout << "Values used: " << hg.v1 << ", " << hg.v2 << endl;
                cout << "Sorry, you don't get to plat any more.
    ";
                break;
            }
        }
        cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
            throw bad_hmean(a,b);
        return 2.0 * a * b / (a + b);
    }
    
    double gmean(double a, double b)
    {
        if (a < 0 || b < 0)
            throw bad_gmean(a,b);
        return std::sqrt(a * b);
    }
    

    ) Unwinding the stack
    In C++, a function normally returns to the function that calls it, with each function liberating its automatic variables as it terminates. If an automatic variable is an object, the destructor for that object is automatically called.
    Suppose you now terminate a function via throw. After a function was terminated by throw, it ends immediately and pass back control to main().While freeing memory on the stack, instead of stopping at the first return address on the stack, the program continues freeing the stack until it reaches a return address which resides in the try block. This is to say, although a function is terminated by throw, the program would free all intermediate functions' automatic variables on the stack, otherwise the intermediate function's variables would be left unfreed.
    This technique is called unwinding the stack, which is extremely important to exceptions.
    Here comes the code illustrating unwinding the stack:

    // error5.cpp -- unwinding the stack
    #include <iostream>
    #include <cmath>
    #include <string>
    #include "exc_mean.h"
    
    class demo
    {
    private:
        std::string word;
    public:
        demo(const std::string & str)
        {
            word = str;
            std::cout << "demo " << word <<" created
    ";
        }
        ~demo()
        {
            std::cout << "demo " << word << " destroyed
    ";
        }
        void show() const
        {
            std::cout << "demo " << word << " lives!
    ";
        }
    };
    
    double hmean(double a, double b);
    double gmean(double a, double b);
    double means(double a, double b);
    
    int main()
    {
        using std::cout;
        using std::cin;
        using std::endl;
    
        double x, y, z;
        {
            demo d1("found in block in main()");
            cout << "Enter two numbers: ";
            while (cin >> x >> y)
            {
                try
                {
                    z = means(x,y);
                    cout << "The mean mean of " << x << " and " << y << " is " << z << endl;
                    cout << "Enter next pair: ";
                }
                catch (bad_hmean & bg)
                {
                    bg.mesg();
                    cout << "Try again.
    ";
                    continue;
                }
                catch (bad_gmean & hg)
                {
                    cout << hg.mesg();
                    cout << "Values used: " << hg.v1 << ", " << hg.v2 << endl;
                    cout << "Sorry, you don't get to play any more.
    ";
                    break;
                }
            }
            d1.show();
        }
        cout << "Bye!
    ";
        cin.get();
        cin.get();
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
            throw bad_hmean(a,b);
        return 2.0 * a * b / (a + b);
    }
    
    double gmean(double a, double b)
    {
        if (a < 0 || b < 0)
            throw bad_gmean(a,b);
        return std::sqrt(a * b);
    }
    
    double means(double a, double b)
    {
        double am, hm, gm;
        demo d2("found in means()");
        am = (a + b) / 2.0;
        try
        {
            hm = hmean(a,b);
            gm = gmean(a,b);
        }
        catch (bad_hmean & bg)
        {
            bg.mesg();
            std::cout << "Caught in means()
    ";
            throw; // rethrow the exception
        }
        d2.show();
        return (am + hm + gm) / 3.0;
    }
    

    ) More exception features
    In previous code, while throwing and exception, you used this code:

    try
    {
        super();
    }
    catch (problem & p)
    {
        ...
    }
    ...
    void super()
    {
        ...
        throw problem(); //notice!
    }
    

    You used throw problem() here because the compiler would always generate a temporary copy while throwing exceptions. Another reason is that you could use base-class reference to point to derived-class objects, so a reference could handle all exception objects in the hierarchy. Meanwhile, the order of catch blocks should also be considered, you shoule put derived-class catch blocks in advance of base-class catch blocks, because the program first reaches the derived-class catch block, and it the type matches, it will run the code and ignore the remaining, empowering you to treat exception objects of different classes separately.

    )The exception class
    C++ incorporates exception class to support exception features. Generally, logic_error and runtime_error classes derives from exception, which separately contains some sort of exception classes.
    The logic_error class contains:
    1 domain_error class, just as functions have domain and range, if you violate that you could throw a domain_error object.
    2 invalid_argument class, when your function receives data which is not desirable or not fit the rules, you could throw a invalid_argument object.
    3 length_error class, indicating that not enough space is available for the desired action.
    4 out_of_bounds class, indicating indexing errors, such as array index out of range.
    Each of these classes mentioned above have a constructor.
    You could also derive your own exception objects from logic_error or runtime_error classes.##15.3 Exceptions
    ) Calling abort()
    When you already know the conditions of failure, you may detect them and call abort() if true to terminate the program directly, without even returning to main():

    // error1.cpp -- using the abort() function
    #include <iostream>
    #include <cstdlib>
    double hmean(double a, double b);
    
    int main()
    {
        double x, y, z;
        std::cout << "Enter two numbers: ";
        while (std::cin >> x >> y)
        {
            z = hmean(x, y);
            std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl;
            std::cout << "Enter next set of numbers <q to quit>: ";
        }
        std::cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
        {
            std::cout << "untenable arguments to hmean()
    ";
            std::abort();
        }
        return 2.0 * a * b / (a + b);
    }
    

    ) Returning an error code
    A program could also use a function's return value to indicat a problem. In fact, every numeric value could be a valid return type:

    #include <iostream>
    #include <cstdlib>
    double hmean(double a, double b);
    
    int main()
    {
        double x, y, z;
        std::cout << "Enter two numbers: ";
        while (std::cin >> x >> y)
        {
            z = hmean(x, y);
            std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl;
            std::cout << "Enter next set of numbers <q to quit>: ";
        }
        std::cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
        {
            std::cout << "untenable arguments to hmean()
    ";
            std::abort();
        }
        return 2.0 * a * b / (a + b);
    }
    

    ) The exception mechanism
    1 Throwing an exception : by using keyword throw followed with a string or an object, tells a program to jump to somewhere else.
    2 Catching an exception with handler: by using keyword catch followed with a block of code, which means that if specific error is encountered, then code within the catch block would be executed.
    3 Using a try block: identifies a block of code which might activate certain errors, usually followed by several catch blocks
    Here comes an example illustrating the mechanism of exceptions:

    #include <iostream>
    #include <cstdlib>
    double hmean(double a, double b);
    
    int main()
    {
        double x, y, z;
        std::cout << "Enter two numbers: ";
        while (std::cin >> x >> y)
        {
            z = hmean(x, y);
            std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl;
            std::cout << "Enter next set of numbers <q to quit>: ";
        }
        std::cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
        {
            std::cout << "untenable arguments to hmean()
    ";
            std::abort();
        }
        return 2.0 * a * b / (a + b);
    }
    

    Noteworthy:
    1 If any statement in the try block leads to an exception being thrown, the catch block after this will handle the exception. If there are multiple catch blocks after a try block, the compiler will search for the correct handler with the correct type which corresponds to what has been thrown.
    2 If a program executes code in try block without any exceptions being thrown, it skips the catch blocks after it and jumps to the very next line of code.

    ) Using objects as exceptions
    In the previous example, the throw follows a string. But most of time you want it to throw an object, because objects could store information and indicate which kind of error it is specifically. Here comes an example implementing the function of calculating harmonic mean and geometric mean of two numbers, also providing two type of exceptions:

    // exc_mean.h -- exception classes for hmean(), gmean()
    #include <iostream>
    #include <cmath>
    
    class bad_hmean
    {
    private:
        double v1;
        double v2;
    public:
        bad_hmean(double a = 0, double b = 0) : v1(a), v2(b) {}
        void mesg();
    };
    
    inline void bad_hmean::mesg()
    {
        std::cout << "hmean(" << v1 <<", " << v2 << "): " << "invalid arguments: a = -b
    ";
    }
    
    class bad_gmean
    {
    public:
        double v1;
        double v2;
        bad_gmean(double a = 0, double b = 0) : v1(a), v2(b) {}
        const char * mesg();
    };
    
    inline const char * bad_gmean::mesg()
    {
        return "gmean() arguments should be >= 0
    ";
    }
    
    double hmean(double a, double b);
    double gmean(double a, double b);
    int main()
    {
        using std::cout;
        using std::cin;
        using std::endl;
    
        double x, y, z;
        cout << "Enter two numbers: ";
        while (cin >> x >> y)
        {
            try
            {
                z = hmean(x, y);
                cout << "Harmonic mean of " << x << " and " << y << " is " << z << endl;
                cout << "Geometric mean of " << x << " and " << y << " is " << gmean(x,y) << endl;
                cout << "Enter next set of numbers <q to quit>: ";
            }
            catch (bad_hmean & bg)
            {
                bg.mesg();
                cout << "Try again.
    ";
                continue;
            }
            catch (bad_gmean & hg)
            {
                cout << hg.mesg();
                cout << "Values used: " << hg.v1 << ", " << hg.v2 << endl;
                cout << "Sorry, you don't get to plat any more.
    ";
                break;
            }
        }
        cout << "Bye!
    ";
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
            throw bad_hmean(a,b);
        return 2.0 * a * b / (a + b);
    }
    
    double gmean(double a, double b)
    {
        if (a < 0 || b < 0)
            throw bad_gmean(a,b);
        return std::sqrt(a * b);
    }
    

    ) Unwinding the stack
    In C++, a function normally returns to the function that calls it, with each function liberating its automatic variables as it terminates. If an automatic variable is an object, the destructor for that object is automatically called.
    Suppose you now terminate a function via throw. After a function was terminated by throw, it ends immediately and pass back control to main().While freeing memory on the stack, instead of stopping at the first return address on the stack, the program continues freeing the stack until it reaches a return address which resides in the try block. This is to say, although a function is terminated by throw, the program would free all intermediate functions' automatic variables on the stack, otherwise the intermediate function's variables would be left unfreed.
    This technique is called unwinding the stack, which is extremely important to exceptions.
    Here comes the code illustrating unwinding the stack:

    // error5.cpp -- unwinding the stack
    #include <iostream>
    #include <cmath>
    #include <string>
    #include "exc_mean.h"
    
    class demo
    {
    private:
        std::string word;
    public:
        demo(const std::string & str)
        {
            word = str;
            std::cout << "demo " << word <<" created
    ";
        }
        ~demo()
        {
            std::cout << "demo " << word << " destroyed
    ";
        }
        void show() const
        {
            std::cout << "demo " << word << " lives!
    ";
        }
    };
    
    double hmean(double a, double b);
    double gmean(double a, double b);
    double means(double a, double b);
    
    int main()
    {
        using std::cout;
        using std::cin;
        using std::endl;
    
        double x, y, z;
        {
            demo d1("found in block in main()");
            cout << "Enter two numbers: ";
            while (cin >> x >> y)
            {
                try
                {
                    z = means(x,y);
                    cout << "The mean mean of " << x << " and " << y << " is " << z << endl;
                    cout << "Enter next pair: ";
                }
                catch (bad_hmean & bg)
                {
                    bg.mesg();
                    cout << "Try again.
    ";
                    continue;
                }
                catch (bad_gmean & hg)
                {
                    cout << hg.mesg();
                    cout << "Values used: " << hg.v1 << ", " << hg.v2 << endl;
                    cout << "Sorry, you don't get to play any more.
    ";
                    break;
                }
            }
            d1.show();
        }
        cout << "Bye!
    ";
        cin.get();
        cin.get();
        return 0;
    }
    
    double hmean(double a, double b)
    {
        if (a == -b)
            throw bad_hmean(a,b);
        return 2.0 * a * b / (a + b);
    }
    
    double gmean(double a, double b)
    {
        if (a < 0 || b < 0)
            throw bad_gmean(a,b);
        return std::sqrt(a * b);
    }
    
    double means(double a, double b)
    {
        double am, hm, gm;
        demo d2("found in means()");
        am = (a + b) / 2.0;
        try
        {
            hm = hmean(a,b);
            gm = gmean(a,b);
        }
        catch (bad_hmean & bg)
        {
            bg.mesg();
            std::cout << "Caught in means()
    ";
            throw; // rethrow the exception
        }
        d2.show();
        return (am + hm + gm) / 3.0;
    }
    

    ) More exception features
    In previous code, while throwing and exception, you used this code:

    try
    {
        super();
    }
    catch (problem & p)
    {
        ...
    }
    ...
    void super()
    {
        ...
        throw problem(); //notice!
    }
    

    You used throw problem() here because the compiler would always generate a temporary copy while throwing exceptions. Another reason is that you could use base-class reference to point to derived-class objects, so a reference could handle all exception objects in the hierarchy. Meanwhile, the order of catch blocks should also be considered, you shoule put derived-class catch blocks in advance of base-class catch blocks, because the program first reaches the derived-class catch block, and it the type matches, it will run the code and ignore the remaining, empowering you to treat exception objects of different classes separately.

    )The exception class
    C++ incorporates exception class to support exception features. Generally, logic_error and runtime_error classes derives from exception, which separately contains some sort of exception classes.
    The logic_error class contains:
    1 domain_error class, just as functions have domain and range, if you violate that you could throw a domain_error object.
    2 invalid_argument class, when your function receives data which is not desirable or not fit the rules, you could throw a invalid_argument object.
    3 length_error class, indicating that not enough space is available for the desired action.
    4 out_of_bounds class, indicating indexing errors, such as array index out of range.
    Each of these classes mentioned above have a constructor.
    You could also derive your own exception objects from logic_error or runtime_error classes.

  • 相关阅读:
    SecureCRT显示乱码的解决办法
    Django如何安装指定版本
    转战简书
    NSmutableArray 的实现原理机制
    字符编码笔记:ASCII,Unicode 和 UTF-8
    [每天记录一个Bug]Cell中由于block加载网络请求产生的复用
    提示的简易写法
    价格不同字体大小的富文本实现方式
    星星的模块封装类 IDSStarsScoreView
    性别年龄的模块封装类 IDSGenderLeviNamedView
  • 原文地址:https://www.cnblogs.com/fsbblogs/p/9973300.html
Copyright © 2020-2023  润新知