开发者

Please disabuse me of exceptions handling problem [closed]

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance. Closed 11 years ago.

Many programmers like to us开发者_如何学Pythone exceptions to react to exceptional circumstances (like runtime errors) in their program. To more clearly state my question, I post two code snippets as follows. My question is "try...catch block is more safer, power and smarter than doing that by programmers themselves with regard to error checking and handling?" I want to have your kindly sugesstions. Thank you in advance!

  1. Using try...catch block

    std::map<std::string, Array2D<unsigned short>* > DataPool;
    
    try
    {  
       for (int i = 0; i < 4; i++)
       {   
           std::stringstream ss;
           ss << "I" << i;
           std::string sKey = ss.str();
    
           // Insert an element into the map container
           DataPool.insert(std::make_pair(sKey, new Array2D<unsigned short>(2288, 2288))); 
       }
    }
    catch (bad_alloc&)
    {  
        cout << "Error allocating memory." << endl;
    
        // Free allocated memory
        for (std::map<std::string, Array2D<unsigned short>* >::iterator it = DataPool.begin(); it != DataPool.end(); it++)
           delete ( *it ).second;
    
       DataPool.clear();
    }
    
  2. Handle error by myself

    std::map<std::string, Array2D<unsigned short>* > DataPool;
    Array2D<unsigned short>* pBuffer = NULL;    
    
    for (int i = 0; i < 4; i++)
    {   
        std::stringstream ss;
        ss << "I" << i;
        std::string sKey = ss.str();
    
        pBuffer = new Array2D<unsigned short>(2288, 2288);
    
        if (pBuffer == NULL)
        {  
           // Free allocated memory
           for (std::map<std::string, Array2D<unsigned short>* >::iterator it = DataPool.begin(); it != DataPool.end(); it++)
               delete ( *it ).second;
    
           DataPool.clear();
           break;
        }
    
        // Insert an element into the map container
        DataPool.insert(std::make_pair(sKey, pBuffer)); 
    }
    


At least for the code snippets you have above, the answer is simple: the first one is "safer" (other code problems notwithstanding), because the second snippet doesn't do what you think it does.

The error checking in the second one will not work, because new will never return null in reasonably standards-conforming C++ compilers. new is required by the C++ standard to throw bad_alloc when allocation fails. So handling bad_alloc exceptions thrown by new is the only way to check for new failures.

Note that I'm talking with respect to the C++ standard and the code snippets you have in your question. C++ compiler implementations may have options that deviates from this behavior, which are (should) be fully documented. As Ben Voigt has mentioned, std::nothrow can be used to make new return NULL instead.


Neither of these is recommended.

Learn RAII. Understand how it provides exception safety much better than try/catch/finally. Then use it.

By all your measures of better (safer, more powerful/flexible, smarter), RAII is superior to try/catch.


Your first problem is that you are not using RAII. RAII is not only the basis of correct resource handling in C++, it is also nearly required for writing exception safe code.

Your small example does not really show the advantages of exception handling very well, precisely because it is so small that it does not need the non-local control flow provided by exceptions. In larger programs it is common that the code that causes a problem and the code that knows how to react to a problem are a long distance away from each other, and exceptions give a clean general method for reporting problems with execution to code further up the stack. In some situations exceptions are also useful for other control flow that is not related to an error occurring, but which warrants an alternate exit from a piece of code.

With RAII your example would look something like this:

std::map<std::string, std::unique_ptr<Array2D<unsigned short> > > DataPool;
for (int i(0); i < 4; ++i) {
    std::stringstream ss;
    ss << "I" << i;

    std::string key(ss.str());
    std::unique_ptr<Array2D<unsigned short> > value(
        new Array2D<unsigned short>(2288, 2288));

    DataPool.insert(std::make_pair(key, value));
}

Note that there is no manual cleanup whatsoever, because it is all managed by the magic of RAII. All of the resources that need cleanup are put into objects that automatically manage their cleanup on scope exit in an operation the cannot throw. This means that when the scope is exited these resources are guaranteed to have been cleaned up. I am using std::unique_ptr in this example for brevity, but if your system does not support it yet then you could also use a class that wrapped the std::map and ensured that all inserted values were subsequently deleted.

The "Error allocating memory." message is unlikely to be useful in this part of the code, but the calling code may have some idea about what the correct action is. This is because the calling code has more context about the actual problem that the code is trying to solve than this small snippet, which should be written to be useable in a variety of different situations. Being useful in a variety of different situations dictates that it should attempt do do what is required of it, and simply report to the calling code any problems that it has in doing so (rather than printing to some stream, which may be entirely inappropriate).

Return codes can also allow code to report problems to its caller, but using return codes precludes returning values from functions, and is also incompatible with the construction model in C++ (a constructor in C++ must either create a working object or throw an exception). Return codes are also likely to be lead to very unreadable code where the majority of the code is related to error handling rather than the actual problem that the code is trying to solve. Another issue with return codes is that they can be less efficient than exceptions if the exception throwing path happens rarely enough (Caveat lector --Don't take my word for it! If you are concerned about performance then you should be measuring the actual performance of your code).


try ... catch would make your code runs slower. And it's a best practice to do error handling manually first, so that you can control every error aspects may happen (and make decision what to do with the error)

In C#, usually all top-level functions are usually wrapped inside a try ... catch, but this must do in combination with error handling first.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜