zoukankan      html  css  js  c++  java
  • 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.

  • 相关阅读:
    enter事件处方方法
    vue 关于父组件无法触发子组件的事件的解决方法
    iview表格高度自适应只需要三步即可
    子组件触发父组件的方法
    组件之间的方法
    年与日时分秒各种格式 补0位的方法
    //定义一个函数,删除首个数组元素
    input标签内容改变的触发事件
    jQuery表单校验jquery.validate.js的使用
    jQuery BlockUI 实现锁屏
  • 原文地址:https://www.cnblogs.com/fsbblogs/p/9973300.html
Copyright © 2011-2022 走看看