Contents  1-5  6-11  12-16  17-21  22-27   28-33  34 - 38  39-46  Projects   MFC

   Contact
   Search
   C
   C++
   Visual Basic
   Java
   JavaScript
   DHTML
   Style Sheets
   About
   Active X
   TDC Binding
   PHP
   Perl and CGI
   Flash
   XML
   SQL
   Messages
   Chat
   MCSE
   Linux
   Cabling   
   ActionScript
   Downloads
   E-Cards   
 
    
    

Static Member Data - Static member data is shared among all instances of a class.  In other words, if there were a
Monster class, and if "int Count" were declared to be static, and there were 20 Monster objects, there would be only
one instance of the integer Count among all 20 Monster objects.  Static member data is a compromise between global
data (available throughout program) and member data (available only to each object.  A static data member belongs to the class rather than the object - they exist one per class throughout the class.  Static members can be accessed without having an object if they have been declared in the class and they are public.  They can be used as counters across instances of a class.  Since they are not part of an object, their declaration does not allocate memory.  They must be defined and initialized outside the declaration of the class, though their prototype may be declared inside the class. 

"The real problem is not whether machines think but whether men do." - B. F. Skinner
"
A person who WON'T think has no advantage over one who CAN'T think." - Paul Lutus

 

//static data members
#include <iostream.h>

class Monster
{
      public:
      Monster(int strength = 1):itsStrength(strength)
      {
  //Every time a Monster object is created, we will increment the static data member in the constructor.
        HowManyMonsters++; }
      virtual ~Monster() { HowManyMonsters--; }

     
//Accessor Methods
      virtual int GetStrength() { return itsStrength; }
      virtual void SetStrength(int strength) { itsStrength = strength; }

    
 //Static data member, so it must be initialized OUTSIDE the class
      static int HowManyMonsters;

      private:
      int itsStrength;
};

//Static member data, must be initialized OUTSIDE the class.  Only one for all Monster objects.
int Monster::HowManyMonsters = 0;

int main()
{  
    int i;
    const int MaxMonsters = 5;
   
//MonsterHouse is an array of 5 Monster pointers
    Monster *MonsterHouse[MaxMonsters];

    for(i = 0; i<MaxMonsters; i++)
       
//Create a new Monster on the heap and have this element point to it.
        MonsterHouse[i] = new Monster(i*100); 

    for(i = 0; i<MaxMonsters; i++)
    {
         cout << "There are ";
         cout << Monster::HowManyMonsters;
         cout << " Monsters left!\n";
         cout << "Deleting the one which has strength ";
         cout << MonsterHouse[i]->GetStrength();
         cout << ".\n";

        
//Delete Monster object to clean up.  Initialize it to 0 to avoid accidents.
         delete MonsterHouse[i];
         MonsterHouse[i] = 0;
    }

    return 0;
}
Output:
There are 5 Monsters left!
Deleting the one which is 0 years old
There are 4 Monsters left!
Deleting the one which is 1 years old
There are 3 Monsters left!
 
Deleting the one which is 2 years old
There are 2 Monsters left!
Deleting the one which is 3 years old
There are 1 Monsters left!
Deleting the one which is 4 years old

The declaration of "HowManyMonsters" does not define an integer - unlike non-static members, no storage space is set aside.  The variable must be declared and defined outside since, it is not in the object instance.  It is a common mistake to forget to define static member variables of classes outside the class.  This will generate a compiler error. 

Static member functions - Static member functions, like static member data, exists not in the instance of the object but in scope of class.  They can be called without having an object instance of the class.  They exist outside the class and are singly shared by all objects of the class.  Static member functions have no "this" pointer, and so they can not be declared "const".  It is important to note that, static member functions cannot access any non-static member variables.  This is because a class's member data variables are accessed via member functions using the "this" pointer.   The example below serves as an illustration of a static member function - and a company with a high turnover rate :) (wink wink):

//static data members
#include <iostream.h>

class Employee
{
      public:
      Employee(int age = 1):itsAge(age){HowManyEmployees++; }
      virtual ~Employee() { HowManyEmployees--; }

      virtual int GetAge() { return itsAge; }
      virtual void SetAge(int age) { itsAge = age; }

     
//Public accessor method for private data.  Return type must also include static.
      static int GetHowMany() { return HowManyEmployees; }

      private:
      int itsAge;
     
//HowManyEmployees is private, so it must be accessed via a public accessor method this time.
      static int HowManyEmployees;
};

int Employee::HowManyEmployees = 0;


//Declare EmployeeCount function
void EmployeeCount();


int main()
{
    int i;
    const int MaxEmployees = 6;
    Employee *EmployeeGroup[MaxEmployees];

    for
(i = 0; i<MaxEmployees; i++)
    {
        EmployeeGroup[i] = new Employee(i);
        EmployeeCount();
    }

    for ( i = 0; i<MaxEmployees; i++)
    {
         delete EmployeeGroup[i];
         EmployeeGroup[i] = 0;
         EmployeeCount();
    }

    return
0;

}

void EmployeeCount()
{
     cout << "There are " << Employee::GetHowMany() << " Employees alive!\n";
}
 
Output:
There are 1 Employees working here.
There are 2 Employees working here.
There are 3 Employees working here.
There are 4 Employees working here.
There are 5 Employees working here.
There are 6 Employees working here.
There are 5 Employees working here.
There are 4 Employees working here.
There are 3 Employees working here.
There are 2 Employees working here.
There are 1 Employees working here.
There are 0 Employees working here.

The EmployeeCount() function can access the public static accessor even though it has no access to an instane of an Employee object.  This brings us to the subject of containment.

Containment - Containment refers to the member data of one class that includes within it the objects of another class. The outer class is said to  CONTAIN the inner class, hence the name.  An employee class might contain string objects, and these would be instance of another class, the String class.  The file below will later become our header file.

//Containment, Operator overloading and a peek inside the string class
#include <iostream.h>
#include <string.h>

class String
{
      public:
     
//3 overloaded constructors
      String();
      String(const char *const);
      String(const String &);

     
//Destructor
      ~String();

     
//Overloaded C++ operators
      char & operator[](int offset);
      char operator[](int offset) const;
      String operator+(const String&);
      void operator+=(const String&);
      String & operator= (const String &);

        //Here we overload the datastream operator (<<) to work with cout. It returns an ostream object.
        //We must use a friend function declared outside the class.

      friend ostream & operator<<(ostream & theStream, String & theString);


     
//General accessors
      int GetLen()const { return itsLen; }
      const char * GetString() const { return itsString; }
     

        //Static member data - must be defined outside the class.
      static int
ConstructorCount;

      private:
      String (int); // private constructor
      char * itsString;
      int itsLen;
};
//----------------------------------------------------------------------------------

//1st overloaded constructor definition. This default constructor creates string of 0 bytes.

String::String()
{
        itsString = new char[1];
        itsString[0] = '\0';
        itsLen=0;

           cout << "\tDefault string constructor.\n";
        ConstructorCount++;
}


//2nd overloaded constructor. It is a private (helper) constructor, used only by class methods
//for creating a newstring of the required size. It is null filled.

String::String(int len)
{
        itsString = new char[len+1];
        int i;

        for(i = 0; i<=len; i++)
            itsString[i] = '\0';

        itsLen=len;

 
         cout << "\tString(int) constructor\n";
        ConstructorCount++;
}

//3rd Overloaded Constructor
String::String(const char * const cString)
{
        itsLen = strlen(cString);
        itsString = new char[itsLen+1];
        int i;

        for(i = 0; i<itsLen; i++)
            itsString[i] = cString[i];

        itsString[itsLen]='\0';


               cout << "\tString(char*) constructor\n";
        ConstructorCount++;
}

//Copy constructor
String::String (const String & rhs)
{
        itsLen=rhs.GetLen();
        itsString = new char[itsLen+1];
        int i;

       
//Copy the char's one by one from one array to the next.
        for( i = 0; i<itsLen;i++)
             itsString[i] = rhs[i];

        itsString[itsLen] = '\0';
          
        cout << "\tString(String&) constructor\n";
        ConstructorCount++;
}

//Destructor
String::~String ()
{
        delete [] itsString; 
//delete the entire array of char
        itsLen = 0;
       
        cout << "\tString destructor\n";
}


//Overloaded C++ operator equals, frees existing memory, then copies string and size.
String & String::operator=(const String & rhs)
{
         if(this == &rhs)
            return *this;

         delete [] itsString;

         itsLen=rhs.GetLen();
         itsString = new char[itsLen+1];
         int i;

         for(i = 0; i<itsLen;i++)
             itsString[i] = rhs[i];

         itsString[itsLen] = '\0';

         return *this;

         cout << "\tString operator=\n";
}



//Overloaded C++ operator non-constant offset, returns a reference to a character so it can be changed.
char & String::operator[](int offset)
{
       if (offset > itsLen)
           return itsString[itsLen-1];
       else
           return
itsString[offset];
}


//Overloaded C++ operator constant offset, for use with const objects. (see copy constructor)
char String::operator[](int offset) const
{
     if(offset > itsLen)
         return itsString[itsLen-1];
     else
         return
itsString[offset];
}

//Overloaded C++ operator +, creates a new string by adding current string to rhs.
String String::operator+(const String & rhs)
{
       int totalLen = itsLen + rhs.GetLen();
       int i,j;
       String temp(totalLen);

       for(i = 0; i<itsLen; i++)
           temp[i] = itsString[i];

       for(j = 0; j<rhs.GetLen(); j++, i++)
           temp[i] = rhs[j];

       temp[totalLen]='\0';

       return temp;
}


//Overloaded C++ operator +=,changes current string, returns nothing.
void String::operator+=(const String& rhs)
{
     int rhsLen = rhs.GetLen();
     int totalLen = itsLen + rhsLen;
     int i,j;

     String temp(totalLen);

     for(i = 0; i<itsLen; i++)
         temp[i] = itsString[i];

     for(j = 0; j<rhs.GetLen(); j++, i++)
         temp[i] = rhs[i-itsLen];

     temp[totalLen]='\0';

     *this = temp;
}


//Overloaded C++ operator << - declared as a friend function above inside the class.
//Returns an object of type ostream.

ostream & operator<< ( ostream & theStream, String & theString)
{
          theStream << theString.itsString;
          return theStream;
}

//ConstructorCount is static, so it must be defined here outside the class.

int String::ConstructorCount = 0;

void main()  {

     String string1;
     String string2;

     string1 = "Those slick NSA G-men. They\'re always watching us!";

     string2 = string1;

     cout << endl << endl;
     cout << "The contents of string1 are:\n" << string1;
     cout << endl << endl;
     cout << "The contents of string2 are:\n" << string2;
     cout << endl << endl;

}

Output:
Default string constructor.
Default string constructor.
String(char*) constructor
String destructor

The contents of string1 are:
Those slick NSA G-men. They're always watching us!

The contents of string2 are:
Those slick NSA G-men. They're always watching us!

String destructor
String destructor

In this example, we have defined a header class to act as our own string class.  Though not exactly the standard library string class, this give us a glimpse of what goes on behind the scenes of the standard string class.  Notice that all copying and sorting is done through loops that cycle through array elements, one by one.  This illustrates the concept that a string is composed of a collection of char values stored as elements in an array.  You will notice a new keyword, "friend".  Let's talk about that for a minute.

Friend classes - Friend classes are for classes created together or entwined in a set.  Creating friend classes expose member data or functions to another class that should only be visible to objects of that class.  It revokes the "private:" declaration.  Friendship is not transferred, inherited or commutative.  Use friend classes sparingly, use public accessor methods instead whenever possible.

Friend functions - Friendship can be granted to functions of a class, rather than a whole class.  It is better to declare member functions of the other class to be friends rather than the entire class.  In this situation, we declared the overloaded operator<< function a friend class so that it could be accessed and manipulated by ostream.  Also note the static member variable "ConstructorCount" is declared.  It is initialized outside the class, as per static member data requirements.  This variable is incremented as each string constructor is called. 

In the class listed below, we will make use of our custom String class.  To compile this project, you will need to create a project with 1 source file and 1 header file.  Add the header file and call it "StringClass.h", and paste all of the code in the program listed in the table above into this file, minus the main() function and all of the code between its brackets.  Use #include to add <iostream.h> and <string.h>.  Then add the source file - call it whatever you wish.  Then paste the code in the table below into that second file.  It will contain our main() function.  Use #include to include the "StringClass.h" header file you created previously.  Then compile and link the project.

#include "StringClass.h"

class Employee
{
      public:
     
//Overloaded constructors
      Employee();
      Employee(char *, char *, char *, long);
     
//Destructor
      ~Employee();

     
//Copy constructor
      Employee(const Employee&);

      Employee & operator= (const Employee &);

    
 //Accessor methods
      const String & GetFirstName() const { return itsFirstName; }
      void SetFirstName(const String & fName) { itsFirstName = fName; }

      const String & GetLastName() const { return itsLastName; }
      void SetLastName(const String & lName) { itsLastName = lName; }

      const String & GetAddress() const { return itsAddress; }
      void SetAddress(const String & address) { itsAddress = address; }

      void SetSalary(long salary) { itsSalary = salary; }
      long GetSalary() const { return itsSalary; }

      private:
      String itsFirstName;
      String itsLastName;
      String itsAddress;
      long itsSalary;
};

//Overloaded constructor definitions
Employee::Employee():
itsFirstName(""),
itsLastName(""),
itsAddress(""),
itsSalary(0)
{}

Employee::Employee(char * firstName, char * lastName,
char * address, long salary):
itsFirstName(firstName),
itsLastName(lastName),
itsAddress(address),
itsSalary(salary)
{}

//Copy constructor definition
Employee::Employee(const Employee & rhs):
itsFirstName(rhs.GetFirstName()),
itsLastName(rhs.GetLastName()),
itsAddress(rhs.GetAddress()),
itsSalary(rhs.GetSalary())
{}

//Destructor definition
Employee::~Employee() {}

//Overloaded assignment operator =. Copies all class's data members to object passed in.
Employee & Employee::operator= (const Employee & rhs)
{
        if(this == &rhs)
           return *this;

        itsFirstName = rhs.GetFirstName();
        itsLastName = rhs.GetLastName();
        itsAddress = rhs.GetAddress();
        itsSalary = rhs.GetSalary();

        return *this;
}

int main()
{
    Employee Charles("Charles","Germany","44 Alamanda Drive", 20000);
    Charles.SetSalary(50000);

    String LastName("Gaines");

    Charles.SetLastName(LastName);
    Charles.SetFirstName("Robert");

    cout << endl << endl;
    cout << "Name: ";
    cout << Charles.GetFirstName().GetString();
    cout << " " << Charles.GetLastName().GetString();
    cout << ".\nAddress: ";
    cout << Charles.GetAddress().GetString();
    cout << ".\nSalary: " ;
    cout << Charles.GetSalary();
    cout << endl << endl;

    return 0;
}

Output:
Name: Robert Gaines
Address: 44 Alamanda Drive
Salary: 50000


Here, an Employee object is created and 4 values are passed in to initialize it.  The Employee accessor method, SetSalary(), is called with a const value of 50000.  Then a string is created and and initialized using a C++ string constant.  This string object is then used as an argument to the SetLastName() accessor method.  SetFirstName() is called with another string constant.   Notice that Employee does not have a function "SetFirstName()" that takes a character string as its argument.  Instead, SetFirstName() requires a const string reference.  The compiler resolves this, since it knows how to make a string from a constant character string.  It was told how to do this when we overloaded the assignment operator with the function:
Employee & operator= (const Employee &);.  Although string objects and data members are CONTAINED in the Employee class, employee objects do not have any special access to them.  The accessor functions of the string class must provide the interface, as the data members are private. 

Filtering access to contained members - The string class provides the operator+.  Then the Employee class blocks the operator+ from being called on Employee objects by declaring that all string accessors, such as "GetFirstName()", return a const reference.  Operator+ can't be a const function because it changes the object it is called on.  Therefore the  following would cause a compile time error:

String buffer = Charles.GetFirstName() + Charles.GetLastName();

The accessor method GetFirstName() returns a const string, and we can't call operator+ on a const object.  To fix this, we must overload the GetFirstName() function to be non-const.  Changing the return value is not sufficient, to overload the function name we must also change the constancy of the function itself.  Example:

const String & GetFirstName() const { return itsFirstName; }    //overloaded
String and GetFirstName() { return itsFirstName; }             
//overloaded

The cost of containment - The user of the Employee class pays a price in memory and performance each time a string object is constructed or a copy of Employee is made.  This is another issue of copying by value vs. copying by reference.  When Employee objects are passed by value, all their CONTAINED string objects are copied as well, and their copy constructors are called.  This is expensive, it takes memory and time.  When Employee objects are passed by reference/pointer, this overhead is saved.  Work hard NEVER to pass anything larger than 4 bytes by value.  If it exceed 4 bytes, try to pass it by reference.


©2004 C. Germany