Back to the WFC main page

Win32 Foundation Classes Coding Guidelines


$Revision: 8 $

Software Development In General

Never, Never, Never Believe What Microsoft Says

Microsoft is chasing a whole bunch of rabbits at the same time. They don't deliberately set out to screw you up but they just can't seem to help it.

Always, Always, Always Believe What Microsoft Does

Never Completely Design a Program Before You Start Coding

Code Ain't Art

Choose To Live

Rarely Abbreviate

Trust No One


C++ Coding Conventions

Don't overload operators

One of the neat things about C++ is the ability to redefine what operators mean. Like most power, it corrupts those who use it. When you must define operators for your classes, keep it simple. For example, the assignment operator (=).

Remember, source code is supposed to communicate ideas from one human to the next. If you redefine lots of operators then the next guy to come along and use your code will not know what is going on.

Also, don't put any logic into operator implementations. Have them call existing member methods. For example, the assignment operator (=) should call Copy(). This keeps functionality in one place instead of many. You can overload this one function in a child class and not have to worry about all the other entry points being covered.

In General


What: Don't use friend.
Why: This stems from my object-purist attitude. Friends break the rules of object oriented design. This means your code will be susceptable to legal bugs (i.e. you are following the rules of the language which leads to undesired results). Friend functions allow you to circumvent the protections built into the class.


What: Don't use initializers in constructors.
Why: When using intializers, you never know when they are going to get called. Things get undefined when you start calling functions. The problem is the C++ specification doesn't make it clear exactly when initializers fire. All we know is that they happen before the body of the constructor is executed. Once you reach the body of the constructor, all constructors have been called. This behavior is defined. When given a choice, I'll go with defined behavior every time. This becomes a real problem when you switch compilers. Since the initializer behavior is undefined, nothing a compiler chooses to do is wrong. You can get very different results from different compilers which leads to debugging parties.
Example:
// Bad Example
class A
{
   private:

      int m_X;

   public:

      A( int i ): m_X( i ) {};

      int GetX( void ) const { return( m_X ); }
      void SetX( int x ) { m_X = x; }
};

class B : public A
{
   public:

      B() : A( GetY() ) {};

      int GetY( void ) const { return( GetX() + 1 ); }
};

// Good Example
class A
{
   private:

      int m_X;

   public:

      A( int i ){ m_X = i; }

      int GetX( void ) const { return( m_X ); }
      void SetX( int x ) { m_X = x; }
};

class B : public A
{
   public:

      B(){ SetX( GetY() ); }

      int GetY( void ) const { return( GetX() + 1 ); }
};

What: Initialize variables when they are declared. When memory is freed, set the pointer to NULL.
Why: Having variables in a known state greatly reduces the chance for bugs. Invalidating pointers after freeing the memory they point to greatly eases the identification of problems. There's nothing trickier than tracking down an errant pointer. If the pointer is set to NULL and then dereference, your program will blow up. This means it is easy to track downa and squash the bug.
Example:
// Bad Example

void some_function( int x_coordinate )
{
   char * buffer;

   if ( x_coordinate > 100 )
   {
      buffer = new char[ 1024 ];
   }
   else if ( x_coordinate < 100 )
   {
      buffer = new char[ 513 ];
   }

   if ( buffer != NULL )
   {
      delete buffer; // If x_coordinate was equal to 100, BLAMO!
   }
}

// Good Example

void some_function( int x_coordinate )
{
   char * buffer = NULL;

   if ( x_coordinate > 100 )
   {
      buffer = new char[ 1024 ];
   }
   else if ( x_coordinate < 100 )
   {
      buffer = new char[ 513 ];
   }

   if ( buffer != NULL )
   {
      delete buffer;
   }
}

What: Never rely on defaults.
Why: Defaults change. Never assume void as an argument list or return type for a function. Never assume class members are private.
Example:
// Bad Example
class string
{
   int m_Length;

   public:

      string();
      int GetLength();
};

// Good Example

class CString
{
   private:

      int m_Length;

   public:

     CString();
     int GetLength( void );
};

What: Do one job in one place.
Why: This is one of my pet peeves. Implement functionality in one place then have all other entry points to this functionality call the one that implements it. Copying of objects is a good example. There are three times when you want to copy the contents of one object into another:
  1. Copy Constructor
  2. Copy Method
  3. Assignment operator
Instead of implementing the copy in three places, do it in one and have the others call it. You should have an = operator for every class that has a copy constructor.
Example:
// Bad Example

class CMyValue
{
   protected:

      int m_Value;

   public:

      CMyValue() { m_Value = 0; };
      CMyValue( const CMyValue& source ) { m_Value = source.m_Value; };
     ~CMyValue() { m_Value = 0; };

      void Copy( const CMyValue& source ) { m_Value = source.m_Value; };
      void SetValue( int new_value ) { m_Value = new_value; };
      CMyValue& operator=( const CMyValue& source ) { m_Value = source.Value; };
};

// Good Example

class CMyValue
{
   protected:

      int m_Value;

   public:

      CMyValue() { SetValue( 0 ); };
      CMyValue( const CMyValue& source ) { Copy( source ); };
     ~CMyValue() { SetValue( 0 ); };

      void Copy( const CMyValue& source ) { SetValue( source.m_Value ); };
      void SetValue( int new_value ) { m_Value = new_value; };
      CMyValue& operator=( const CMyValue& source ) { Copy( source ); };
};

Naming Conventions

Coding Considerations

Return to Sam's Home Page