0

Many programmers like to use 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)); 
    }
    
derekerdmann
  • 17,696
  • 11
  • 76
  • 110
GoldenLee
  • 737
  • 2
  • 13
  • 28

4 Answers4

3

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.

In silico
  • 51,091
  • 10
  • 150
  • 143
  • What does `new` return when you run out of memory if you disable exceptions? – zneak Jul 08 '11 at 02:33
  • Dear In silico, Thank you for your quick reply. What a new operator retuen if there is no sufficient memory to be allocated for an array? – GoldenLee Jul 08 '11 at 02:35
  • @GoldenLee, with exceptions enabled, it throws an `std::bad_alloc` exception (thus mandating the use of `try`/`catch` blocks). With them disabled, I don't know. – zneak Jul 08 '11 at 02:38
  • @GoldenLee: It doesn't return, it throws an exception. If you want a NULL return in case of error, use `new (std::nothrow) `... – Ben Voigt Jul 08 '11 at 02:38
  • @zneak: If you disable exceptions, you are no longer coding in C++. Hense, you have to ask whoever created the malformed imitation you are actually coding against. In other words, it is up to your compiler. – Dennis Zickefoose Jul 08 '11 at 02:43
  • @Ben Voigt: How to use new (std::nothrow)? Shiould I use in my second code snippet like this the following: pBuffer = new(std::nothrow) Array2D(2288, 2288); ? – GoldenLee Jul 08 '11 at 02:53
  • @Ben Voigt: I have not ever seen this kind use of "new" operator. – GoldenLee Jul 08 '11 at 02:55
  • @GoldenLee: That snippet is exactly how to use non-throwing `new`. Be sure to `#include ` to make that work. – Ben Voigt Jul 08 '11 at 03:57
3

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.

Community
  • 1
  • 1
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
2

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

Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • Dear Mankarse, Thank you for your detailed explanation on RAII. I know a little bit of RAII, unique_ptr, shared_ptr, and thus have not ever used them in my practical programming. You said if I would use unique_ptr in map container, manual cleanup for allocated memory is not required. Suppose the DataPool is declared as a class-level variable, it's not necessary for me to free its unique_ptr values in the destructor or anywhere out of the scope? – GoldenLee Jul 08 '11 at 03:23
  • It is not necessary. Before an object is created in C++ all of its sub-objects are created, and after an object is destroyed all of its sub-objects are destroyed (in reverse order). If it was in a class then you would not need any particular code in the destructor of the class, because the destructor for the map would be automatically invoked after the destructor for the class anyway (and this would release the resources). – Mankarse Jul 08 '11 at 03:29
  • On the other hand, if you used raw pointers in the map and wanted the class to automatically release them, then you would need special code, not only in the destructor, but also in the copy constructor, assignment operator, and in a variety of other places where you handle the raw pointers. – Mankarse Jul 08 '11 at 03:33
-1

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.

longbkit
  • 1,218
  • 1
  • 13
  • 20
  • That would be right for real-time systems controlling laser-shooting arms, but controlling every aspects of every error seems like a very large burden not worth implementing in most office programs. – zneak Jul 08 '11 at 02:32
  • Also, can you explain precisely what the runtime does in a `try`/`catch` block that slows down the code? – zneak Jul 08 '11 at 02:36
  • the best practice for office programs would be like as I said: all top-level functions (GUI event handlers for instance) should be wrapped in a try ... catch, but error checking should be do manually as well because you could give more detail error message to user. – longbkit Jul 08 '11 at 02:36
  • You're talking like error checking and exception catching are two very different things. How would you explain the difference between "manual error checking" and "catching a specific exception"? – zneak Jul 08 '11 at 02:40
  • Thank you longbkit. I used to do error handling manually. I've found many open source softwares like to use "try...catch block" to react runtime error handling. That's why my question saised here. – GoldenLee Jul 08 '11 at 02:41
  • @zneak that is when controlling business functions, and using try ... catch in every branches of code. Today people pay a lot attentions in the speed, and if a real exceptions is happening it can hurts performance. And, "manually error checking" is like checking whether an object is not null before working with it. – longbkit Jul 08 '11 at 02:43