• Home >>  Java >>  Programming Tips
  • developing applications that always decease gracefully.


  • Rating : Excellent[0]   Very Good[0]   Average[0]   Below Average[0]   Poor[0]
  • Pub Date: 10/14/2008
  • admin [ View Profile ]

  • Abstract
  • It is always a dream for every programmer under the sun, to write programs that never smash. So this article presents some guidelines in trapping exceptions which can kill applications, and thereby ensuring the graceful exit of a program. I have included some code snippets that show how to trap exceptions.Guidelines for writing C++ applicationsCoping with object creation and destructionWhenever you create an object in the heap (using th......

  • Introduction
  •  Sponsored Links
  • introduction

    it is always a dream for every programmer under the sun, to write programs that never smash. so this article presents some guidelines in trapping exceptions which can kill applications, and thereby ensuring the graceful exit of a program. i have included some code snippets that show how to trap exceptions.

    guidelines for writing c++ applications

    coping with object creation and destruction

    • whenever you create an object in the heap (using the new operator), always remember to delete the same (using the delete operator).

      you can create an object of type t dynamically by using a new expression such as:

      t* obj = new t(); //creates a new object obj of type t in heap

      the original c++ rule says, new operator always returns a null pointer if memory could not be allocated. so it’s a good practice to check for null after creating the object, and before using it.

      when you want to deallocate a heap object, you can use the delete expression such as:

      delete obj; //deletes the object obj from heap, and frees the memory

      before deleting an object, always check for null. (this is because, may be you are trying to delete an object that is already deleted.)

      t* obj = new t(); //creates a new object obj of type t in heap
      
      if( obj != null )
      {
      // perform operations on obj
      
      }
      {
      if( obj != null )
      delete obj; //deletes the object obj from heap, and frees the memory
      
      }

      all the member variables of a class should be created and initialized in the constructor itself. most of the cases its not possible, so always ensure that objects are used only after they are initialized.

    • when creating an array of objects, you must have a constructor in place, that can be invoked without arguments.

      t* objs = new t[n]; //creates an array of n objects of type t.

      to deallocate the array, you must use this form of delete expression:

      delete [] objs;
    • practice the habit of declaring virtual destructors in base classes.

      when you try to delete an object of a derived class through a pointer to a base class, only the base class destructor is executed, leading to all kinds of problems, like memory and other resource leaks.

      class a {
      . . .
      };
      class b : public a {
      . . .
      };
      a* pb = new b;
      delete pb;    // resource leak

      a common rule is that if a class has a virtual function, it probably needs a virtual destructor as well—and once we decide to pay the overhead of a vtable pointer, subsequent virtual functions will not increase the size of the object. so, in such a case, adding a virtual destructor doesn't add any significant overhead. so it’s always nice to have a virtual destructor in base classes.

    using exceptions

    when i started to use exceptions, it rapidly became evident that exceptions are more difficult to use effectively than it first seems. in fact, the more i explored how exceptions can interact with non-exception handling code, the more convinced i became that exceptions may be the most difficult feature of c++ to use. so there are some guidelines, which you should follow while using exceptions.

    • when you propagate an exception, try to leave the object in the state it had when the function was entered.

      this is the golden rule of exception handling. it is not going to do much good for a program to handle an exception if the program has gone into an invalid state as a result of the exception propagating to the point where it could be handled. i personally feel that writing code that meets this goal is very difficult. i put this guideline first because it is the target that should always be the ultimate goal. i will only give a few simple suggestions here.

      1. make sure your const functions really are const.
      2. perform exception prone operations early.
      3. avoid side effects in expressions that might propagate exceptions.
      try {
      stack[esp++] = element; 
      } catch (...) {
      --esp; // reset state on exception
      
      }
    • if you cannot leave the object in the same state it had when the function was entered, try to leave it in a good state.

      the reason why this is required is that the client might not be paying as much attention to exception handling, as they should. client might handle the exception, but not realize that the object is no longer valid. in such cases, always make sure that any further attempt to use the object will be rejected.

    • if you cannot leave the object in a "good" state, make sure the destructor will still work.

      if guideline 1 and 2 are not possible, as a last resort, try to leave the object in such a state that it can always be safely destroyed. never forget that as an exception propagates, it will unwind the stack frame of every function it propagates through. many times, this will invoke the destructor of the very object that threw the exception.

    • avoid resource leaks.

      the most obvious type of resource leak is a memory leak, but memory is not the only resource that can leak. in one sense, a resource leak is just another example of an inconsistent state. to avoid memory leaks, always try to use auto_ptr<> template class provided by the standard c++ library. there are three different instances where exceptions can cause resource leaks: in a constructor, in a destructor, and in a function (whether a member of a class or not).

      when an exception propagates from a constructor, the partial object that has been constructed so far is destroyed. if necessary, any memory allocated from the free store for the object is also released. if, during the construction, a resource (such as memory) is obtained directly and not as part of a sub-object whose destructor will release the resource, then a resource leak can occur. this situation can be dealt with using try block to catch the exception and attempting to release the memory. so initialize the pointers to null, and delete them all in the catch block.

      when we have resource leaks in destructors: as a general rule, we do not want to throw (or propagate) exceptions from destructors. nevertheless, we cannot always prevent it. in situations like this, try using auto_ptr<>, and transfer ownership of the resources to the temporary objects. when the destructor body exits, these objects are destroyed, deleting their pointers.

    • do not catch any exception you do not have to.

      actually there is an old c rule of error handling, that states: "do not test for any error condition you do not know how to handle”. in c++ it means, it is a waste of time (yours and the computers) to catch an exception you do not know how to handle. i can give a few suggestions on how to behave on a situation like this.

      1. always use a catch(...) block to cope with propagating exceptions.
      2. do not "handle" any exception that cannot be "fixed."
      3. do not throw exceptions from a destructor body.
      4. if you get stuck, call terminate() or exit().

      before exceptions, the normal way to abnormally terminate a program was by calling the c library function abort(). in the new c++ world, we should call terminate() instead. terminate() just calls terminate_handler. in the default case, this calls abort(), but the user can replace the default terminate_handler with a program specific version. so call terminate() to allow any user defined terminate_handler to run.

    • do not hide exception information from other parts of the program that might need it.

      the purpose of exceptions is to pass information from the point where an error is detectable to a point where the error can be handled. if you throw a different exception rather than re-throwing the original exception, you want to make sure you are increasing the possibility of the exception being handled, by doing so.

      1. always re-throw the exception caught in a catch (...) clause.
      2. re-throw a different exception only to increase the level of abstraction or capability.
      3. make sure one catch block does not hide another.
    • unless you have a strong guarantee of exception safety (guideline 1), assume you must destroy the object after any exception.

      when you handle an exception, you correct whatever was wrong and then redo the failed operation. in order for this to work, you have to be sure that the operations which failed left their objects in the state they had before the operations were attempted. this is guideline 1, and is what the c++ standard calls the "strong guarantee". if you cannot say for certain that guideline 1 is being followed, then all bets are off. if guideline 2 has been followed, you might be in a position to reuse the object, but in general the only really safe thing to do is destroy the object and start over.

    • always catch an exception by reference.

      this doesn’t require much clarification, because i assume that everyone already knows this.

      i presume, i have gone a bit detail into exceptions, so i am concluding this topic of exception-handling with a few words. “exception handling is obviously a powerful feature of the c++ language. although you can walk down the path of not using exceptions at all, their benefits far outweigh the costs”.

    a little bit unix flavor

    the unix operating system uses signals as a means of notifying a process that some event, often unrelated to the process's current activity, has occurred that requires the process' attention. signals are delivered to a process asynchronously; a process cannot predict when a signal might arrive. failing to properly handle various signals would likely cause your application to terminate, when it receives such signals. when we say that "signals are being handled", we mean that our program is ready to handle such signals that the operating system might be sending it (such as signals notifying that the user asked to terminate it, or that a network connection we tried writing into, was closed, etc.). a typical signal handler would look like this:

    /* this is the signal handler */
    void catch_int(int sig_num)
    {
    /* re-set the signal handler again to catch_int, for next time */
    signal(sigint, catch_int);
    /* do some action */
    fflush( stdout );
    cout <<"some message"<<endl;
    }

    now a few thumb rules for writing a signal handler.

    1. make it short - the signal handler should be a short function that returns quickly. instead of doing complex operations inside the signal handler, it is better that the function will raise a flag (e.g. a global variable, although these are evil by themselves) and have the main program check that flag occasionally.
    2. proper signal masking - don't be too lazy to define proper signal masking for a signal handler, preferably using the sigaction() system call. it takes a little more effort than just using the signal() system call, but it'll help you sleep better at night, knowing that you haven't left an extra place for race conditions to occur.
    3. careful with "fault" signals - if you catch signals that indicate a program bug (sigbus, sigsegv, sigfpe), don't try to be too smart and let the program continue, unless you know exactly what you are doing (which is a very rare case) - just do the minimal required cleanup, and exit, preferably with a core dump (using the abort() function).
    4. careful with timers - when you use timers, remember that you can only use one timer at a time, unless you also use the vtalrm signal. if you need to have more than one timer active at a time, don't use signals, or devise a set of functions that will allow you to have several virtual timers using a delta list of some sort.
    5. signals are not an event driven framework - it is easy to get carried away and try turning the signals system into an event-driven driver for a program, but signal-handling functions were not meant for that. if you need such a thing, use some framework that is more suitable for the application.

    in windows, also we can trap for many different exceptions such as access_violation, stack_overflow, array_bounds_exceeded, datatype_misalignment, flt_denormal_operan, flt_divide_by_zero, flt_inexact_result, flt_invalid_operation, flt_overflow, flt_stack_check, flt_underflow, int_divide_by_zero, and int_overflow using the try- except blocks.

    using debug functions

    • using assert

      it is not uncommon for bugs to linger because a section of code is not behaving as you think it is. i've stared at a line of code for minutes on end before i've been able to find a simple typo. more complicated bugs can be murder to find because, it often turns out, i'm assuming i see something that isn't there. one of the most powerful techniques for overcoming this problem is the assert macro.

      many compilers offer an assert() macro as part of their default library. the assert() macro is designed to assert a fact about your program-that is, to document (and test) what you think is true at a given moment in the history of your program. here's how it works: you assert something by passing it as an argument to the assert macro. the macro takes no action if you are correct, but it aborts your program and puts up an error message if you are not correct-if the asserted "fact" is not true.

      for example,

      assert( x > 10 ) //if x is greater than 10, nothing happens
      
      
       //otherwise program halts and an error message is displayed.
      
      

      the assert macro is a powerful debugging tool, but for it to be acceptable in a professional development environment, it must not create a performance penalty nor increase the size of the executable version of the program. to accomplish this, the preprocessor collapses the assert macro into no code at all if debug is not defined. thus, in your development environment, you can use assert to find your bugs and misunderstandings, but when your code ships, there is no penalty.

    • using class invariants

      most classes have some conditions that should always be true. for example, it may be true that your circle object should never have a radius of zero, or that your animal should always have an age greater than 0 and less than 100. it can be very helpful to declare an invariants() method that returns true only if all if these conditions are true. you can then assert(invariants()) at the start and completion of every class method.

    conclusion

    "a robust program is resistant to errors -- it either works correctly, or it does not work at all; whereas a fault tolerant program must actually recover from errors".

    credits

    while working on this, i have referred the following articles:

    history

    • date submitted: 05 september 2003.
  • RATE THIS ARTICLE :
  •  
    • Latest Comments:
      • Add a comment
      • Title:
      • Comment
      •  
    Other popular Programming Tips articles:
    • VSS: protocol handler for Visual SourceSafe

      Imagine that you could open a document in a Visual SourceSafe database with one mouse click.

    • How to use google and other tips for finding programming help

      Code Project is a wonderful resource. Certainly, my career as a developer is built on the help I got here learning MFC in late 1999. However, it's increasingly the case that a lot of the questions on our forums are easily answered by a simple google search, so I'm writing this article to explain how

    • broadcast a message to multiple instances of an application

      This application describes how to broadcast a message to multiple instance of an application using SendMessageNotify API. DetailsFollow the following steps to broadcast message :-Register the message function and get its id using RegisterWindowMessageAPI. This function will return a unique id for th

    • about rss

      Want more traffic? Searching for an easy way to distribute your news? You need an RSS news feed. Thousands of web sites today use RSS as a "what's new" mechanism to drive traffic their way. RSS provides an easy way to monitor fresh content. RSS feeds highlight new material so that you don'

    • Developing applications that always decease gracefully.

      It is always a dream for every programmer under the sun, to write programs that never smash. So this article presents some guidelines in trapping exceptions which can kill applications, and thereby ensuring the graceful exit of a program. I have included some code snippets that show how to trap exce

    • unit test utility: restoring database state after each unit test execution

      Need: Lets start with an example, suppose we are writing unit tests for following two cases: GetCustomersByIdGreaterThan() AddCustomer() Initial state of database is like We run first unit test case for GetCustomersByIdGreaterThan()[Test]public void CanGetCustomersByIdGreaterThan(){const int CUSTOME

    • complexconverter - make configuration still more flexible

      Normal|0; 0; 765; 321|2|Knoten0|2|Knoten1|0|False|Knoten2|3|Knoten4|0|False|Knoten5|0|False|Knoten6|0|False|False|True|Knoten7|2|Knoten8|0|False|Knoten9|0|False|False|90|90|90|90|2|4|01|02|03|04|4|11|12|13|14Inside ComplexConverterComplexConverter has a problem, when set up, or conversion-code is ch

    • driving or automating gui applications

      Sometimes you want or need to control (or automate the use of) an application but the application provides no automation API (command line interface or CLI, COM component, .NET API DLL, web service, etc.), so what do you do? You're left with (graphical) user interface (GUI) automation. Although, in

    • unit testing starter - vs.net 2008

      Unit Testing starter - VS.NET 2008 Hi Everybody,As you know, VS.NET 2008 comes with unit testing integrated within. To start with a testdriven approach, we need to understand how unit testing works in VS.NET 2008. Many of usmight be using already available tools / frameworks for this, viz - NUnit (M

    • cost reduced swap

      void main() { int i=10,j=20; j=i+j-(i=j); } Here the two variables are swapped in a single statement . The cost is reduced than the known swapping methods. Temporary variables are not used.addition and subtraction only.can be used for any data types and this is efficient than the normal methods.time

    About Us |Contact us |Site Map |Csharp |Visual C / C++ |Visual basic |Java |SQL |Linux / Unix |Ajax
    ©2007-2018 CodeCoolest.com. Ptolive.cn Asp.net source code All Rights Reserved.