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

   Contact
   C
   C++
   Visual Basic
   Java
   JavaScript
   DHTML
   Style Sheets
   About
   Normalization
   Active X
   TDC Binding
   PHP
   Perl and CGI
   Flash
   XML
   SQL
   Chat
   MCSE
   Linux
   Cabling   
 

   
 
    
    

Note: Updated!  See top of introduction page c1.html for using a C++ .Net compiler for these standard C++ tutorials.

Advanced pointer manipulation involves creating objects on the free store - you can create a pointer to any object.
Pointers work for classes and user-defined data types as well as they work for built-in data types.  If you declare an
object of type Cat, you can declare a pointer to that class and instantiate a Cat object on the free store just as you
can make one on the stack.   Syntax:   
Cat  *KittyPointer  =  new Cat;

"Even if there is only one possible unified theory, it is just a set of rules and equations.  What is it that breathes fire into the equations and makes a universe for them to describe?" - Stephen W. Hawking

This calls the default constructor (constructor that takes no parameters).  It is called whenever an object is created either on the stack or the free store.   When you call delete on a pointer to an object on the free store the object's destructor is called before the memory is released.  This gives your class a chance to clean up as it does for objects on the stack.  Example:

//Creating objects on the free store 
#include <iostream.h>

//-----------------------------------------------------------------------------------------------------------

class Cat  { 

        public: 
        Cat(); 
        ~Cat(); 

       private: 
       int itsAge; 
}; 
  


//Define Cat constructor
Cat::Cat() 
{ 
       cout << "Constructor called. A kitty is created!\n"; 
       itsAge = 1; 
} 

//Define Cat destructor  
Cat::~Cat() 
{ 
       cout << "Destructor called.  Alas, poor kitty . . .\n"; 
} 
  
//-----------------------------------------------------------------------------------------------------------

int main()  {
 
     cout << "Creating a local stack Cat, calling him Felix...\n";
 
     Cat Felix; 

     cout << "\n\nCreating a Cat on the heap and pointing to it with:\n";
     cout << "Cat *KittyPointer = new Cat...\n"; 

     Cat *KittyPointer = new Cat; 

     cout << "\n\ndelete KittyPointer...\n"; 

     delete KittyPointer;

     //Assign to NULL to be safe
     KittyPointer = 0; 

     cout << "\n\nExiting, observe Felix's untimely demise...\n";

     return 0; 
} 
Output:
Creating a local stack Cat, calling him Felix...
Constructor called. A kitty is created!

Creating a Cat on the heap and pointing to it with:

Cat * KittyPointer = new Cat...
Constructor called. A kitty is created!

delete KittyPointer...
Destructor called.  Alas, poor kitty . . .

Exiting, observe Felix's untimely demise...
Destructor called.  Alas, poor kitty . . .

Felix was created on the stack which caused the constructor to be called.  The Cat object pointed to by KittyPointer is created on the heap and the constructor is called again.  When delete is called on KittyPointer and destructor is called.   When the main() function ends, Felix goes out of scope and the destructor is called again.

When creating objects on the stack, we must access data members and functions by using the dot operator "."  In the instance of the Cat class, this would apply to all Cat objects created locally.  To access the Cat objects on the free store(heap), you also use the dot operator but you must dereference the pointer when you call the dot operator "."  on the object pointed to by the pointer.  To access the GetAge member function of an object on the heap (free store), you would write:

(*KittyPointer).GetAge();

Parenthesis are used to ensure that KittyPointer is dereferenced before the GetAge() member method is accessed.  One can also use the "points-to operator" - "->", a shorthand operator for indirect access.   Example:

#include <iostream.h> 

//-----------------------------------------------------------------------------------------------------------


class Cat  { 

        public: 
        Cat() {itsAge = 2; } 
        ~Cat() {} 

        int GetAge() const { return itsAge; } 
        void SetAge(int age) { itsAge = age; } 
  
        private: 
        int itsAge; 
}; 
  
//-----------------------------------------------------------------------------------------------------------

int main()  {
 
     Cat *Sunshine = new Cat;
     Cat *Tiger = new Cat;
  
     cout << "Sunshine is " << Sunshine->GetAge() << " years old\n"; 
     Sunshine->SetAge(7); 

     Tiger->SetAge(5);
     cout << "Tiger is " << Tiger->GetAge() << " years old\n"; 

     delete Sunshine;
     delete Tiger;

     return 0; 
} 

Output:
Sunshine is 2 years old.
Tiger is 5 years old.
 

The Cat object, Sunshine, is instantiated on the free store.  The default constructor sets its age to 2.  The GetAge() member method is called.  It is a pointer and the points-to operator "->" is used to access the member data and functions since the Cat object is on the heap rather than the stack.  The same holds true for the SetAge() method and the GetAge() method.

One or more of the data members of a class can be a pointer to an object on the free store.  The memory can be allocated in the class constructor or in one of its methods and it can be deleted in its destructor.  Example:

// Pointers as data members of a class 
#include <iostream.h>

//--------------------------------------------------------------------------------------------------------------------

class Monster  { 

       public: 
       Monster(); 
      ~Monster();
 
      int GetStrength() const { return *itsStrength; } 
      void SetStrength(int strength) { *itsStrength = strength; } 
  
      int GetWeight() const { return *itsWeight; } 
      void SetWeight (int weight) { *itsWeight = weight; } 
  
      private: 
      int *itsStrength; 
      int *itsWeight; 
}; 
  

//Define Monster constructor outside of class
Monster::Monster() 
{ 
      itsStrength = new int(200);   //create new int on heap with value of 200, then point to it
      itsWeight = new int(5000);    //create new int on heap with value of 5000, then point to it
} 
  
//Let destructor clean up the heap memory we allocated for the object's data member pointers
Monster::~Monster() 
{ 
      delete itsStrength; 
      delete itsWeight; 
} 

//--------------------------------------------------------------------------------------------------------------------
 
int main()  {
 
     Monster *Godzilla = new Monster; 
     cout << "Godzilla is of strength: " << Godzilla->GetStrength() << ".\n"; 

     Godzilla->SetStrength(500); 

     cout << "Godzilla weighs " << Godzilla->GetWeight() << " kilograms!\n"; 
     cout << "Godzilla is eating Tokyo!\n";

    Godzilla->SetWeight(50000);
    cout << "He now weighs " << Godzilla->GetWeight() << "kilograms!"; 
    
    delete Godzilla; 

    return 0; 
} 

Output:
Godzilla is of strength 200.
Godzilla weighs 500 kilograms!
Godzilla is eating Tokyo!
He now weighs 50000 kilograms!


In this example, Monster is declared to have 2 member variables which are pointers to integers.  The constructor initializes the pointers to memory on the free store by creating 2 integer variables on the heap, assigning them default values, and having the 2 data members of the Monster class point to their address.  Default values are supplied if none are passed in.  When the destructor is called, it cleans up the allocated memory using "delete".  Because this is the destructor there is no point in assigning these pointers to null because they will be inaccessible.  The calling function main() is unaware that itsStrength and itsWeight are pointers to memory on the free store.  main() continues to call GetStrength() and SetStrength(). The details of the memory management are hidden in the implementation of the class as they should be.  Godzilla is deleted and its destructor is called.  The destructor deletes each of its member pointers.  If these point to objects of other user-defined classes, their destructors are called as well.

The "this" pointer - Every class member function has a hidden parameter. The "this" pointer points to the individual object.  In each call for SetAge() and GetAge(), the "this" pointer for the object is included as a hidden parameter.  It points to the individual object whose method has been invoked. Usually "this" is not needed.  Occasionally you need to access the object itself (perhaps to return a pointer to the current object).  You don't need to use "this" to access member variables of an object from within methods of that object.  You may explicitly call it, like so, however:

// Using the this pointer 
#include <iostream.h>

//--------------------------------------------------------------------------------------------------------------------
 
class Square  { 

        public: 
        Square(); 
        ~Square(); 

       void SetLength(int length) { this->itsLength = length; } 
       int GetLength() const { return this->itsLength; } 
  
       void SetWidth(int width) { itsWidth = width; } 
       int GetWidth() const { return itsWidth; } 
  
       private: 
       int itsLength; 
       int itsWidth; 
}; 
  

//Define constructor outside of class specification
Square::Square() { 
            
        itsWidth = 100; 
        itsLength = 100; 
} 

//Define destructor
Square::~Square() 
{} 

//--------------------------------------------------------------------------------------------------------------------
   
int main()  { 
  
     Square ASquare; 
     cout << "ASquare is " << ASquare.GetLength() << " feet long.\n"; 
     cout << "ASquare is " << ASquare.GetWidth() << " feet wide.\n"; 
     
     ASquare.SetLength(200); 
     ASquare.SetWidth(200); 

     cout << "ASquare is " << ASquare.GetLength()<< " feet long.\n"; 
     cout << "ASquare is " << ASquare.GetWidth()<< " feet wide.\n"; 
     
    return 0; 
} 

Output:
ASquare is 100 feet long.
ASquare is 100 feet wide.
ASquare is 200 feet long.
ASquare is 200 feet wide.


The SetLength() and GetLength() accessor functions explicitly use the "this" pointer to access the member variables of the Square object. The SetWidth() and GetWidth() accessors do not, rather they implicitly make use of the object's address.  There is no difference in their behavior.  The "this" pointer is a pointer, so it stores the memory address of an object.  This makes it a powerful tool.

Stray pointers - Stray pointers, also called wild or dangling pointers, are created when you call "delete" on a pointer and free the memory that it it points to, then later try to use it again without reassigning it.  This causes crashes and time bombs.  After you delete a pointer, disarm it by setting it to "null" (0).  

const pointers - Constant pointers can't be reassigned.  If "const" is before the asterisk, the object is constant, if "const" comes after the asterisk, the pointer is constant.   Example:

const int * Pointer1;            //the int pointed to is constant, hence a pointer to a constant integer
int * const Pointer2;            //p2 is constant, it can't point to anything else, hence a constant pointer to an integer

const int * PointerOne;       //pointer to a "constant integer". 

The declaration above states that the value that is pointed to can't be changed using this pointer.  In other words, we couldn't write:
*PointerOne = 5;
  this would be flagged as an error.
 

int * const PointerTwo;      //"constant pointer" to an integer.

The declaration above states that the integer can be changed, but PointerTwo can't point to anything else.  In other words, we couldn't write:  PointerTwo = &x.

const int * const PointerThree;        //constant pointer to a constant integer.

The declaration above states that both the value that is pointed to (dereferenced) can't be changed, and that PointerThree can't be reassigned to point to anything else.

Note: When a function is declared as constant the compiler flags as an error any attempt to change data in the object from within that function.  If you declare a pointer to be a const object, the only methods you can call with that pointer are const methods.  Example:

//Using pointers with const methods 
#include <iostream.h> 

//--------------------------------------------------------------------------------------------------------------------
   
class Rectangle { 

       public: 
       Rectangle(); 
       ~Rectangle(); 
   
       void SetLength(int length) { itsLength = length; } 
       int GetLength() const { return itsLength; } 
  
       void SetWidth(int width) { itsWidth = width; } 
       int GetWidth() const { return itsWidth; } 
  
       private: 
       int itsLength; 
       int itsWidth; 
}; 
  
Rectangle::Rectangle():itsWidth(5),itsLength(10) 
{ }
 
  
Rectangle::~Rectangle() 
{} 

//--------------------------------------------------------------------------------------------------------------------
     
int main() 
{ 
     Rectangle *RectanglePointer = new Rectangle;
 
     //Points to a constant rectangle - can't change value
     const Rectangle *ConstantRectangle = new Rectangle;
 
       //Pointer is constant, can't reassign
     Rectangle * const ConstantPointer = new Rectangle; 
  

     cout << "RectanglePointer width: " << RectanglePointer->GetWidth() << " feet\n"; 
     cout << "ConstantRectangle width: " ; 

     cout << ConstantRectangle->GetWidth() << " feet\n"; 
     cout << "ConstantPointer width: " << ConstantPointer->GetWidth() << " feet\n"; 
  
     RectanglePointer->SetWidth(10); 
     // ConstantRectangle->SetWidth(10);  would be an error

     ConstantPointer->SetWidth(10); 
  
     cout << "RectanglePointer width: " << RectanglePointer->GetWidth() << " feet\n";
 
     cout << "ConstantRectangle width: "; 
     cout << ConstantRectangle->GetWidth() << " feet\n";
 
     cout << "ConstantPointer width: " << ConstantPointer->GetWidth() << " feet\n"; 
     
     return 0; 
} 

Output:
RectanglePointer width:       5 feet
ConstantRectangle width:  5 feet
ConstantPointer width:   5 feet
RectanglePointer width:      10 feet
ConstantRectangle width:  5 feet
ConstantPointer width:  10 feet
 

We declare a Rectangle.  We declare the GetLength() member method const. We then declare:

1) a plain, non-constant pointer to a non-constant Rectangle object

2)
ConstantRectangle, which is a non-constant pointer to a constant Rectangle object
3) ConstantPointer, which is a constant pointer to a non-constant Rectangle
object

With ConstantPointer, the pointer is constant and cannot point to anything else, but the Rectangle is not constant and can be changed - the call is o.k.  We print the value of the widths.  RectanglePointer is used to set width of the rectangle to 10.   ConstantRectangle would be used but it was declared to point to a const Rectangle.  Because of this it can not legally call a non-const member function and it is commented out.   ConstantPointer calls SetWidth(10). 

Note: When you declare an object to be const you are declaring that the "this" pointer is a pointer to a const object.   Constant "this" pointers can only be used with const member functions.


©2004 C. Germany