Simple Programming Tips On C/C++

Discussion in 'C++' started by subhasish, Apr 9, 2006.

  1. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    Declaring References to Functions

    You can declare a reference to a function, just like you can declare a pointer to a function. For example:


    Code:
    void f(int n)
    {
      ++n;
    }
    
    int main()
    {
      void (&rf) (int) = f; //bind rf as a reference to f()
      rf(5); //call f() through its reference
    }
    The major difference between a pointer and a reference to a function is that the latter must always be bound to an existing function (i.e., it may not be null or dangling), and you cannot re-bind another function to it once you have initialized it.
     
  2. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    Hide Function Pointer Declarations With a typedef

    Can you tell what the following declaration means?


    Code:
     void (*p[10]) (void (*)() );
    Only few programmers can tell that p is an "array of 10 pointers to a function returning void and taking a pointer to another function that returns void and takes no arguments." The cumbersome syntax is nearly indecipherable. However, you can simplify it considerably by using typedef declarations. First, declare a typedef for "pointer to a function returning void and taking no arguments" as follows:


    Code:
     typedef void (*pfv)();
    Next, declare another typedef for "pointer to a function returning void and taking a pfv" based on the typedef we previously declared:


    Code:
     typedef void (*pf_taking_pfv) (pfv);
    
    Now that we have created the pf_taking_pfv typedef as a synonym for the unwieldy "pointer to a function returning void and taking a pfv", declaring an array of 10 such pointers is a breeze:


    Code:
    pf_taking_pfv p[10];
     
  3. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    No Pointer Arithmetics Outside Array Bounds!

    Sometimes it is tempting to let a pointer point to a location that is slightly beyond an array's index range. For example, consider the following self-defined array class, which allows you to define arrays whose index ranges do not necessarily start at zero:


    Code:
        template 
         class MyArray
           {
             T*  ptr;
             int f, l;
           public:
             MyArray (int first, int last)
               : f(first), l(last)
               {
                 ptr = new T [last-first+1];  // Why +1?
                 ptr -= first;
               }
             T& operator[] (int index)
               {
                 return ptr[index];
               }
             // Further methods
           };
    
    The trick is simple and elegant and might result in very efficient code: Since the address stored in ptr differs from the address of the real array by the value of first, first need not be subtracted from index in operator[]. A neat trick, right?

    Unfortunately, the C++ standard draft says that a call to operator[] would result in undefined behavior. Even if it runs fine on your system, there are definitely systems on which this is not the case. An expression with pointer arithmetics is well defined only if all involved pointers and all results of partial expressions point to elements of one--and only one--array.
     
  4. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    Pointers to Members of a Template Class

    You can define a pointer to a class template member. For example, you can use the specialization vector<int>:

    Code:
     	
    typedef void (vector< int >::*v_vi_szt) (size_t); // v_vi_szt is used to hide the unwieldy syntax
    v_vi_szt  reserve_ptr = &vector< int >::reserve; 
    The only difference from ordinary pointers to class members is that you are required to use a template specialization, since a template name per se is not a type. In other words, you have to define a separate pointer to member for every specialization used. In the following example, vector <string> specialization is used (no typedef applied in this case):

    Code:
    void (vector< string >::*v_vs_szt) (size_t)  = &vector< string >::reserve; // string  specialization 
     
  5. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    Uses of the ptrdiff_t Data Type

    C and C++ define a special type for pointer arithmetic, namely ptrdiff_t, which is a typedef of a platform-specific signed integral type. You can use a variable of type ptrdiff_t to store the result of subtracting and adding pointers. For example:

    Code:
    #include <stdlib.h>
    int main()
    {
      int buff[4];
      ptrdiff_t diff = (&buff[3]) - buff; // diff = 3
      diff = buff -(&buff[3]); //  -3
    }
    What are the advantages of using ptrdiff_t? First, the name ptrdiff_t is self-documenting and helps the reader understand that the variable is used in pointer arithmetic exclusively. Secondly, ptrdiff_t is portable: its underlying type may vary across platforms, but you don't need to make changes in the code when porting it.
     
  6. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    Prefer References Over Pointers

    Even experienced C++ programmers who have prior experience with C tend to use pointers excessively whereas references may be a better choice. Pointers may result in bugs like the following:


    Code:
    bool isValid( const Date *pDate);
    
    void f()
    {
    	Date *p = new Date(); //default: current date
    
    	//...many lines of code
    
    	delete p;		//p is now a “dangling pointer” 
    	bool valid = isValid(p); //oops! undefined behavior
    	p = NULL;
    	valid = isValid(p)	//ops! null pointer dereferencing; 	
    					//most likely will lead to a crash
    }
    
    The use of references eliminates the notorious bugs related to pointers: null pointer assignment and dangling pointer dereferencing, since a reference is always bound to a valid object:

    Code:
    bool isValid( const Date& date);  //reference version
    
    void f()
    {
    	Date date; //default: current date
    
    	//...many lines of code
    
    	bool valid = isValid(date); //always safe
    	date += 100; //add 100 days
    	valid = isValid(date)	//always safe					
    }
    
     
  7. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    Beware of Aliasing

    Whenever your class contains pointers, references, or handles, you need to define a copy constructor and assignment operator. Otherwise, the compiler-generated copy constructor and assignment operator will result in aliasing, that is to say, the same resource will be used simultaneously by more than one object and will also be released more than once - with disastrous results:

    Code:
    class Document {
    private:
    	FILE *pdb;
    public:
    	Document(FILE *f =NULL) : pdb(f){}  //no user-defined copy constructor or operator=
    	~Document() {fclose(pdb);}
    	//...
    };
    void assign(Documnet d&)
    {
    Document temp("letter.doc");
    d = temp;  //Aliasing; both d and temp now point to the same file
    }//temp's destructor is now automatically called and closes file letter.doc while d is still using it  
    void main() 
    {
    Document doc;
    assign(doc);
    //OOPS! doc now uses a file which has just been closed 
    }//OOPS! doc's destructor is now invoked and closes 'letter.doc' once again
    
     
  8. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    auto_ptr<>: your safe guard against memory leaks

    The Standard Library supplies the class template auto_ptr<> which automatically deallocates heap memory. Likewise, local objects are reclaimed in case of exiting their scope or during "stack unwinding" caused by an exception.

    This technique can avoid memory leakage in the case of uncaught exception or even simplify programming by sparing the hassle of explicitly deleting every object allocated using operator new. The auto_ptr<> class template is declared in the standard <memory> header.
    Code:
     
    #include <memory> //auto_ptr<> declaration
    #include <iostream>
    
    using namespace std;
    
    class Date{ /*...*/};
    
    void DisplayDate()
    {
    	//now create a local object of type auto_ptr<Date> 
    auto_ptr<Date> pd (new Date); //now pd is owned by the template object
    	cout<<pd->DateString();
    
    //note: pd is automatically deleted by the destructor of auto_ptr; it shouldn't be deleted by programmer
    
    }
    
    In other words, the auto_ptr<> instance, pd, can be used like an ordinary pointer to Date but it behaves like a local object in respect to its automatic destruction.
     
  9. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    Why you shouldn't store auto_ptr objects in STL containers

    Most C++ users already know that they shouldn't use auto_ptr objects as elements of STL containers. However, fewer users know exactly why this is so.

    The C++ Standard says that an STL element must be "copy-constructible" and "assignable." These fancy words simply mean you can safely assign or copy one object to another and get two independent and logically identical copies. In particular, the state of the original object shouldn't change when it is copied to the target object.

    This is not the case with auto_ptr, though: copying or assigning one auto_ptr to another makes changes to the original, in addition to the obvious changes to the copy. To be more specific, the original object transfers ownership of the pointer to the target, and the pointer in the original object becomes null. Imagine what would happen if you did something like this:


    Code:
     class Foo{};
      vector  < auto_ptr <Foo> > vf; //a vector of auto_ptr's
      //..fill vf
      int g()
      {
        auto_ptr <Foo> temp = vf[0]; // vf[0] becomes null
      }

    When temp is initialized, the member vf[0] is changed: its pointer becomes null. Any attempt to use that element will cause a runtime crash. This situation is likely to occur whenever you copy an element from the container. Remember that even if your code does not perform any explicit copy or assignment operations, many algorithms (swap(), random_shuffle(), sort(), and many others) create a temporary copy of one or more container elements. Furthermore, certain member functions of the container may create a temporary copy of one or more elements, thereby nullifying them. Any subsequent attempt to the container elements is therefore undefined.

    Several Visual C++ users report that they have never encountered any problems with using auto_ptr in STL containers. This is because the auto_ptr implementation of Visual C++ (all versions thereof) is outdated and relies on an obsolete specification. When Microsoft decides to catch up with the current ANSI/ISO C++ Standard and change their Standard Library accordingly, code that uses auto_ptr in STL containers will manifest serious malfunctions.

    To conclude, you shouldn't use auto_ptr in STL containers. Either use bare pointers or use other smart pointer classes instead of auto_ptr
     
  10. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    Iterators Aren't Pointers

    Suppose you define a the following list object and you need the address of one of its elements:


    Code:
     std::list <int>  li;  
      std::list <int>::iterator  iter = li.begin();
    In many contexts, iter functions like a pointer. However, when you need a real pointer to a container's element, you can't use an iterator:

    Code:
      int func(int * p);  
      int main()
      {
        func(iter); // error, iter is not a pointer to int
      }
    The problem is that in general, iterators aren't pointers. Rather, they are usually implemented as objects. To get a pointer to the element an iterator refers to, you need to "dereference" that iterator and take the address of the result. For example:

    Code:
      int main()
      {
        func( &*iter); // ok
      }
     
  11. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    Does the Compiler Always Create a Default Constructor?

    The compiler creates a default construtor only in the following situations:

    * When the class's base class defines the default constructor.
    * When the class of member objects defines default constructors.
    * When the class has a virtual function.
    * When the class has a virtual base class.

    So for the following class, the compiler does not create a defalut constructor if you do not define one:

    Code:
    class test
    {
    
    	public:
    		void show();
    		void get(int, char*, float);
    	private:
    		int h,
    		char* ch;
    		float f;
    };
    
     
  12. subhasish

    subhasish New Member

    Joined:
    Mar 29, 2006
    Messages:
    35
    Likes Received:
    6
    Trophy Points:
    0
    The #error Preprocessor Directive

    During the preprocessing phase, you can report errors by using the #error preprocessor directive. For example, suppose you have a program that uses 64-bit integers. This program has to be ported to various platforms. Since C++ doesn't define a standard 64-bit int type yet, each compiler uses a different type, and some compilers don't support 64-bit integers at all. A portable program can use the following preprocessor directives to make sure that the use of 64-bit integers is transparent to the target compiler. If the target compiler doesn't support 64-bit integers, the #error directive causes it to produce a diagnostic message that includes a user-supplied string and stops the compilation process:

    Code:
     
    #if defined((__BORLANDC__) || defined(__VISUALC32__)) 
    #  define INT64 __int64 // then use the __int64 type
    #elif defined(__GNUC__) // GCC doesn't support __int64
    #  define INT64 long long // but uses 'long long' instead
    #else
    #  error “Unsupported platform; aborting”
    #endif
     
  13. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
    Comma-Separated Expressions

    [thread=819]Comma-Separated Expressions[/thread]
     
  14. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
    [thread=755]Default Constructors[/thread] thread tells when a default constructor is not called.
     
  15. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
    How the compiler differentiates delete[] and delete?

    [thread=899]How the compiler differentiates delete[] and delete?[/thread]
     
  16. sandesh

    sandesh New Member

    Joined:
    Sep 17, 2007
    Messages:
    4
    Likes Received:
    0
    Trophy Points:
    0
    function pointer problem

    Hello.......
    In the given code I want to use only one function pointer which can call two different type of function .............is it possible ................if yes then how ???


    main()
    {
    // ( )(* fun_point)( );
    int fun1(int,int);
    char fun2(char,char);
    }
     
  17. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
    [COMMENT]sandesh, do not jump into any thread with your query or else you will not see any responses as the visibility of your query is very less. Try creating a new thread for your query if its not related to the thread you have posted into.[/COMMENT]
     
  18. technosavvy

    technosavvy New Member

    Joined:
    Jan 2, 2008
    Messages:
    52
    Likes Received:
    0
    Trophy Points:
    0
    i think this is also a suitable tip which can be put in this thread..

    always do comparision by putting the constant on the left hand side of the exspression.
    e.g.
    Code:
    if (0 == toCompareVariable) {
    /*anything*/
    }
    is a better programming practice than
    Code:
    if (toCompareVariable == 0) {
    /*anything*/
    }
    this way even if someone do a typo and write if (0 = toCompareVariable) ..the compiler will produce an error on compilation itself..and thus we are not supposed to scratch our head finding where is code going wrong in case of if (toCompareVariable = 0)
     
    Last edited: Jan 14, 2008
  19. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
    Thats a very good thing technosavvy to be doing when you are writing codes.
     
  20. asadullah.ansari

    asadullah.ansari TechCake

    Joined:
    Jan 9, 2008
    Messages:
    356
    Likes Received:
    14
    Trophy Points:
    0
    Occupation:
    Developer
    Location:
    NOIDA

    That's Excellent!!! We should take it as practice in our daily coding life to avoid some bugs.
     
    shabbir likes this.

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice