[17] Exceptions and error handling
(Part of C++ FAQ Lite, Copyright © 1991-96, Marshall Cline, cline@parashift.com)


FAQs in section [17]:


[17.1] How can I handle a constructor that fails?

Throw an exception.

Constructors don't have a return type, so it's not possible to use error codes. The best way to signal constructor failure is therefore to throw an exception.

If you don't have or won't use exceptions, here's a work-around. If a constructor fails, the constructor can put the object into a "zombie" state. Do this by setting an internal status bit so the object acts sort of like its dead even though it is technically still alive. Then add a query ("inspector") member function to check this "zombie" bit so users of your class can find out if their object is truly alive, or if it's a zombie (i.e., a "living dead" object). Also you'll probably want to have your other member functions check this zombie bit, and, if the object isn't really alive, do a no-op (or perhaps something more obnoxious such as abort()). This is really ugly, but it's the best you can do if you can't (or don't want to) use exceptions.

TopBottomPrevious sectionNext section ]


[17.2] How should I handle resources if my constructors may throw exceptions?

Every data member inside your object should clean up its own mess.

If a constructor throws an exception, the object's destructor is not run. If your object has already done something that needs to be undone (such as allocating some memory, opening a file, or locking a semaphore), this "stuff that needs to be undone" must be remembered by a data member inside the object.

For example, rather than allocating memory into a raw Fred* data member, put the allocated memory into a "smart pointer" member object, and the destructor of this smart pointer will delete the Fred object when the smart pointer dies. The standard class auto_ptr is an example of such as "smart pointer" class. You can also write your own reference counting smart pointer. You can also use smart pointers to "point" to disk records or objects on other machines.

TopBottomPrevious sectionNext section ]


[17.3] How do I change the string-length of an array of char to prevent memory leaks even if/when someone throws an exception?

If what you really want to do is work with strings, don't use an array of char in the first place, since arrays are evil. Instead use an object of some string-like class.

For example, suppose you want to get a copy of a string, fiddle with the copy, then append another string to the end of the fiddled copy. The array-of-char approach would look something like this:

    void userCode(const char* s1, const char* s2)
    {
      // Get a copy of s1 into a new string called copy:
      char* copy = new char[strlen(s1) + 1];
      strcpy(copy, s1);
    
      
// Now that we have a local pointer to freestore-allocated memory,
      
// we need to use a try block to prevent memory leaks:
      try {
    
        
// Now we fiddle with copy for a while...
        
// ...
    
        
// Later we want to append s2 onto the fiddled-with copy:
        
// ... [Here's where people want to reallocate copy] ...
        char* copy2 = new char[strlen(copy) + strlen(s2) + 1];
        strcpy(copy2, copy);
        strcpy(copy2 + strlen(copy), s2);
        delete[] copy;
        copy = copy2;
    
        
// Finally we fiddle with copy again...
        
// ...
    
      } catch (...) {
        delete[] copy;   
// Prevent memory leaks if we got an exception
        throw;           
// Re-throw the current exception
      }
    
      delete[] copy;     
// Prevent memory leaks if we did NOT get an exception
    }

Using char*s like this is tedious and error prone. Why not just use an object of some string class? Your compiler probably supplies a string-like class, and it's probably just as fast and certainly it's a lot simpler and safer than the char* code that you would have to write yourself. For example, if you're using the string class from the standardization committee, your code might look something like this:

    #include <string>           // Let the compiler see class string
    using namespace std;
    
    void userCode(const string& s1, const string& s2)
    {
      
// Get a copy of s1 into a new string called copy:
      string copy = s1;         
// NOTE: we do NOT need a try block!
    
      
// Now we fiddle with copy for a while...
      
// ...
    
      
// Later we want to append s2 onto the fiddled-with copy:
      copy += s2;               
// NOTE: we do NOT need to reallocate memory!
    
      
// Finally we fiddle with copy again...
      
// ...
    }                           
// NOTE: we do NOT need to delete[] anything!

TopBottomPrevious sectionNext section ]


 E-mail the author
C++ FAQ LiteTable of contentsSubject indexAbout the author©Download your own copy ]
Revised Sep 8, 1997