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   
 
    
    

Advanced References and Pointers pass by reference for efficiency.  Each time you pass an object into a function
by value, a copy is made of it.  Each time you return an object from a function by value, another copy is made.  This
will cause you to use more memory than you need to and your programs will run slower.  The size of a user-created
object on the stack is the sum of its member variables, and these in turn can be user-created.  Passing such a massive structure by copying it onto the stack is expensive in memory and performance.  This is because its copy constructor is called so many times.  Passing objects by reference saves unnecessary calls to the copy constructor.

"Not only does God play dice, but... he sometimes throws them where they cannot be seen." - Stephen W. Hawking

copy constructor - called each time a temporary copy of an object is put on the stack. 
copy destructor - if an object is returned by value, a copy of the object must be made and destroyed as well. 

The copy constructor looks like the constructor - it has the same name as the class.  It is a reference or alias to the class.  Note that, in large objects constructor and destructor calls are expensive in speed and memory.  Example:

//Passing pointers to objects

#include <iostream.h>

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

class Monster
{
      public:
      Monster ();               
//constructor
      Monster(Monster&);        
//copy constructor
      ~Monster();               
//destructor
     
      private:
      bool Hungry;
      int Strength; 
};


//Monster constructor defined outside class
Monster::Monster() {
       cout << "Monster Constructor...\n"; }


//Monster copy constructor defined outside class
Monster::Monster(Monster&) {
cout << "Monster Copy Constructor...\n"; }


//Monster destructor defined outside class

Monster::~Monster(){
cout << "Monster Destructor...\n"; }

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

//Declare (prototype) 2 functions with a return type of Monster
Monster PassMonsterByValue(Monster theMonster);
Monster *PassMonsterByPointer(Monster *theMonster);

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

int main()
{
    cout << "Making a monster...\n";

    Monster KingKong;

    cout << "Calling PassMonsterByValue...\n";

    PassMonsterByValue(KingKong);     
//passes by value, copy made, copy constructer used

    cout << "Calling PassMonsterByPointer...\n";

    PassMonsterByPointer(&KingKong);   
//passes by reference, no copy made, copy constructer not used

    return 0;
}

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

//PassMonsterByValue, passes by value
Monster PassMonsterByValue(Monster theMonster)
{
        cout << "Function One. Returning...\n";
        return theMonster;
}


//PassMonsterByPointer, passes by reference using a pointer
Monster *PassMonsterByPointer (Monster *theMonster)
{
        cout << "Function Two. Returning...\n";
        return theMonster;
}
Output:
Making a Monster...
Monster Constructor.
Calling PassMonsterByValue...
Monster Copy Constructor...
Function One. Returning.
Monster Copy Constructor...
Monster Destructor...
Monster Destructor...
Calling PassMonsterByPointer...
Function Two. Returning...
Monster Destructor...

The return value from PassMonsterByValue() is not assigned to any object, so the temporary object created for the return object of the function is thrown away (discarded), calling the destructor and producing the 7th line of output.   This is because we did not have any object to catch the object returned by invoking the function.  Because PassMonsterByValue() has ended its local copy goes out of scope and is destroyed producing (8th line of output.

Program execution returns to the main() function.  PassMonsterByPointer is called, producing 9th line of output.  The parameter here, however, is passed by reference this time, rather than value.  No copy is produced so there is no output.  PassMonsterByPointer() prints a message that produces the 10th line of output.  It also returns the Monster object by reference, so there are no calls to the constructor or destructor.

The program ends and the Monster object, KingKong,  goes out of scope producing one last, final call to the destructor and producing 11th line of output.  By contrast, PassMonsterByValue() produces two calls to the copy constructor and two calls to the destructor because it passes the Monster by value.  FuncitonTwo() produces no calls because it passes the Monster by reference.  Passing a pointer to PassMonsterByPointer() is more efficient, yet it can be dangerous.  PassMonsterByPointer() is not allowed to change the Monster object passed to it, yet it is given the address of the Monster.  This exposes the object to possible change and defeats the protection offered in passing by value.

We can combine the protection of passing by value, where a copy of the object is altered rather than the original, and the efficiency of passing by reference (not creating extra copies and consuming more memory) if we use constant pointers.  Passing a constant pointer prevents calling any non-constant method on Monster and protects the object from change.  Example:

// Passing pointers to objects
#include <iostream.h>

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

class Monster
{
      public:
      Monster();
      Monster(Monster&);
      ~Monster();

     
//Accesor Methods
      int GetStrength() const { return itsStrength; }
      void SetStrength(int Strength) { itsStrength = Strength; }

      private:
      int itsStrength;
};

//Monster Constructor
Monster::Monster()
{
    cout << "Monster Constructor...\n";
    itsStrength = 200;
}

//Monster Copy Constructor
Monster::Monster(Monster&)
{
    cout << "Monster Copy Constructor...\n";
}

//Monster Destructor
Monster::~Monster()
{
    cout << "Monster Destructor...\n";
}

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

//Prototype - return a const pointer to a const Monster and also take one as an argument
const Monster *const PassMonsterByPointer(const Monster *const theMonster);

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

int main()
{
    cout << "Making a monster...\n";

    Monster JollyGreenGiant;
   
    cout << "JollyGreenGiant is ";
    cout << "of strength " << JollyGreenGiant.GetStrength() << ".\n";

    int Strength = 500;

    JollyGreenGiant.SetStrength(Strength);
    cout << "JollyGreenGiant is ";
    cout << "of strength " << JollyGreenGiant.GetStrength() << ".\n";

    cout << "Calling PassMonsterByPointer...\n";

  
 //Must use address of operator when passing object since function uses a pointer
     //At this point, we don't have to catch the return value since it is just an alias of what is passed in

    PassMonsterByPointer(&JollyGreenGiant);

    cout << "JollyGreenGiant is of strength ";
    cout << JollyGreenGiant.GetStrength() << ".\n";

    return 0;
}

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

//PassMonsterByPointer, passes and returns a const pointer so its members can not be changed
const Monster *const PassMonsterByPointer(const Monster *const theMonster) {
 
      cout << "Function Two. Returning...\n";
      cout << "JollyGreenGiant is now of strength " << theMonster->GetStrength();
      cout << ".";
     
      //theMonster->SetStrength(8);
  //Could not do this - members are constant and can't change!

      return theMonster;
}
Output:
Making a monster...
Monster Constructor...
JollyGreenGiant is of strength 200.
JollyGreenGiant is of strength 500.
Calling PassMonsterByPointer...
Function Two. Returning...
JollyGreenGiant is now of strength 500.JollyGreenGiant is of strength 500.
Monster Destructor...

In this case, Monster has added two accessor functions: the const function GetStrength(), and the NOT const function SetStrength().  The member variable itsStrength is private, and so must use these accessor methods.  The copy constructor is never called since the object is passed by reference, not value,  and therfore no copies are made. 

PassMonsterByValue() is not used, PassMonsterByPointer() is called.  The parameter and return value are now declared to take a const pointer to a const object and to return a const pointer to a const object.  Again, parameters and return values are passed by reference, so no copies are made and the copy constructor is not called.  The pointer in PassMonsterByPointer() is now const, so it can not call the non const method SetStrength().  Even though the method itself is not constant, the pointer is constant and the Monster object is constant, making, for all intents and purposes, all of the class's data members and methods constant.  If the call to SetStrength() were not commented out inside PassMonsterByPointer(), the program would not compile.  Objects created in main() are not const,  so JollyGreenGiant can call the SetStrength() method.  The address of this non const object is passed to PassMonsterByPointer(), but because FuntionTwo()'s declaration declares the pointer to be a const pointer the a constant object, the non-constant method would be treated as if it were constant.  This saves a few calls to the constructor and destructor and so is more efficient, and it prevents changing the object passed in, but it is still cumbersome because the objects passed to the function are pointers.  Is there a better way? 

References may be used as an alternative.  The object would be easier to work with if a reference were passed in, rather than a pointer, since you know the object in this case will never be null.   Example:

//Passing references to objects

#include <iostream.h>

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

class Monster
{
      public:
      Monster();
      Monster(Monster&);
      ~Monster();

     
//Accessor methods
      int GetStrength() const { return itsStrength; }
      void SetStrength(int Strength) { itsStrength = Strength; }

      private:
      int itsStrength;
};


//Monster Constructor
Monster::Monster()
{
    cout << "Monster Constructor...\n";
    itsStrength = 300;
}

//Monster Copy Constructor
Monster::Monster(Monster&)
{
    cout << "Monster Copy Constructor...\n";
}

//Monster Destructor
Monster::~Monster()
{
    cout << "Monster Destructor...\n";
}

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

//Prototype - function returns a const reference to a const monster object
const Monster &PassMonsterByReference (const Monster &theMonster);

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

int main()
{
    cout << "Making a Monster...\n";

    Monster Mothra;

    cout << "Mothra is of strength " << Mothra.GetStrength() << ".\n";

    int Strength = 700;

    Mothra.SetStrength(Strength);

    cout << "Mothra is of strength " << Mothra.GetStrength() << ".\n";
    cout << "Calling PassMonsterByReference...\n";
   
   
//No need for address of operator since using references
    PassMonsterByReference(Mothra);

    cout << "Mothra is of strength " << Mothra.GetStrength() << ".";
   
    return 0;
}

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

/
/PassMonsterByReference passes a const reference to a const object
const Monster &PassMonsterByReference (const Monster &theMonster)
{
      cout << "Function Two. Returning...\n";
      cout << "Mothra is now of strength " << theMonster.GetStrength();
      cout << ".";
     
      //theMonster.SetStrength(8);
Could not do this - members are constant!

      return theMonster;
}
Output:
Making a Monster...
Monster Constructor...
Mothra is of strength 300.
Mothra is of strength 700.
Calling PassMonsterByReference...
Function Two. Returning...
Mothra is now of strength 700.Mothra is of strength 700.Monster Destructor...

The output is identical to the previous program. The significant change is that PassMonsterByReference() now takes and returns a const reference to a const object.  Working with references is simpler than working with pointers, yet the same efficiency of not passing by value and safety of const are obtained.  When do we chose to use references and when do we choose to use pointers?

Many C++ programmers strongly prefer references over pointers.  References are cleaner and do a better job at hiding information, data members and methods.  In this way they are more true to OOP theory - data hiding and encapsulation.  Remember that references can not be reassigned, however.   If you need to point first to one object, and then to another, use a pointer.   Again, if you are going to reassign, you can not use references.  Also, references can not be NULL.  If there is any chance that the object in question could be null, use must a pointer.

One example concerns the use of "new".  If "new" cannot allocate memory on the free store, it returns a null pointer.  Since a reference cannot be null, you must not initialize a reference to this memory until you've checked that it is not null.  Though null is preferable for pointer conditions, it is not for references.  Example:

int *IntegerPointer = new int;

if(IntegerPointer != NULL)   
//must check to be sure pointer is not null before assigning to reference
   int &IntegerReference = *IntegerPointer;

A pointer to an int,  pInt is declared and initialized with memory returned by operator "new".  The address of pInt is tested, and if it is not null, pInt is dereferenced.  Dereferencing an int variable creates an int object, and the reference rInt is initialized to refer to that object.  The reference rInt then becomes an alias for the int on the heap created with the operator "new".

Don't return a reference to an object that isn't in scope - don't overdo referencing.  We must ask, "What is the object I am aliasing, and will it still exist every time it is used?".  Here is an example of the danger of returning a reference to an object that no longer exists:

#include <iostream.h>

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

class Employee
{
        public:
        Employee(int age, int SSN);
       ~Employee() {}

      
//Accessor methods
       int GetAge() { return itsAge; }
       int GetSSN() { return itsSSN; }

       private:
       int itsAge;
       int itsSSN;
};


//Define constructor, shorthand way of assigning values through accesors
Employee::Employee(int age, int SSN):
itsAge(age), itsSSN(SSN) {}


//Returns a reference to an employee
Employee &GoingNowhere();

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

int main()
{
    Employee &RefToEmployee = GoingNowhere();
    int age = RefToEmployee.GetAge();
    cout << "RefToEmployee is " << age << " years old!\n";

    return 0;
}

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

Employee &GoingNowhere()
{
     Employee DrewBarrymore(5,9);
     return DrewBarrymore;
}

Output: Compile error - Attempting to return a reference to a local object!

The body of GoingNowhere() declares an object local to the function itself, but nto global, of type Employee, and it initializes its age and SSN through the constructor.  It then returns that local object by reference.  The problem is, when a functoin ends all its data members are destroyed to free up memory.  Its objects do not persist.  Smart compilers will flag this as an error.  When GoingNowhere() returns the local object, DrewBarrymore will be destroyed!  The reference returned by this function will be an alias to a non-existent object, NULL. 

You might be tempted to solve this by having GoingNowhere() create DrewBarrymore on the heap, so that when you return from GoingNowhere(), DrewBarrymore will still exist.  This is also a problem.  What do you do with the memory you have allocated for DrewBarrymore when you are done with it?   Example:

// Resolving memory leaks
#include <iostream.h>

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

class Employee
{
      public:
      Employee(int age, int SSN);
      ~Employee() {}

     
//Accesor methods
      int GetAge() { return itsAge; }
      int GetSSN() { return itsSSN; }

      private:
      int itsAge;
      int itsSSN;
};


//Define constructor, shorthand way of assigning values through accesors

Employee::Employee(int age, int SSN):itsAge(age), itsSSN(SSN) {   }

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

Employee &MemoryLeak();

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

int main()
{
    Employee &RefToEmployee = MemoryLeak();

    int age = RefToEmployee.GetAge();
   
    cout << "RefToEmployee is " << age << " years old!\n";
    cout << "Address of &RefToEmployee: " << &RefToEmployee << endl;

  
//How do you get rid of that memory?

   Employee *CharlesGermany = &RefToEmployee;
   delete CharlesGermany;
  
  
//Uh oh, RefToEmployee now refers to what?  NULL?

   return 0;
}

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

//This function creates an employee object on the heap aliased by a reference
Employee &MemoryLeak()
{
        
//Since CharlesGermany is a pointer, using it without dereferencing
            //it will render a memory address rather than a value!

         Employee *CharlesGermany = new Employee(34,250);
         cout << "Address of CharlesGermany: " << CharlesGermany << endl;
         return *CharlesGermany;
}

Output: 
Address of CharlesGermany: 0x2bf4
RefToEmployee is 34 years old!
Address of &RefToEmployee: 0x2bf4

This will compile, link and appear to work.  But it is a ticking time bomb!  Ahhhhhhhhhhhh!  A memory leak!

MemoryLeak() has been changed so that it no longer returns a reference to a local variable.  Memory is allocated on the free store (that is, the heap), and is assigned to a pointer.  The address that that pointer holds is printed, since it is used without dereferencing, and then the pointer is dereferenced so the Employee object can be returned by reference via the value pointed to by the pointer.  Remember that this object is on the heap, not local to the function, so it does not go out of scope this time when the function ends.

When MemoryLeak() returns its value, it ends and its reference to the Employee object "CharlesGermany", on the heap, is returned via the pointer to a reference to an Employee object in main().  That object is then used to obtain the Employee's age, which is then output.  To prove that the reference declared in main() is referring to the object put on the free store(heap) in MemoryLeak(), the "address of" operator is applied to the reference RefToEmployee, revealing it has the same address as the object "CharlesGermany", on the heap.  It is an alias for this object.  The reference displays the address of the object it refers to, and this matches the address of the object on the heap.

How then, can the memory be freed?   You can't call delete on the reference.  You could create another pointer and initialize it with the address obtained from RefToEmployee.  This deletes the memory and plugs the memory leak, but what then is RefToEmployee referring to after delete is called on the pointer?   A reference must always alias an actual object, if it references a null object the program is invalid.  Remember that a reference should NEVER be NULL.  There are 3 possible solutions:

1) Declare an Employee object and return the Employee from MemoryLeak() by value rather than reference.
2) Declare the Employee object on the heap in MemoryLeak(), but have MemoryLeak() return a pointer to that memory instead
    of a reference.  Then the calling function can delete the pointer when it is done. 
3) Declare the object in the calling function, main(), and pass it to MemoryLeak() by reference. 

When a program allocates memory on the free store, a pointer is returned.  It is imperative that you keep a pointer to that memory, because once the pointer is lost, the memory cannot be deleted and it becomes a memory leak.  Eeeek! A leak! :)  As you pass this block of memory between functions, someone will "own" the pointer.  Typically the value in the block will be passed using references, and the function that creates the memory is the one that deletes it.  However, this is not a rule that is set in stone.

It can be dangerous for one function to create memory and another to free it.  Ambiguity about who owns the pointer can lead to either forgetting to delete a pointer at all, or deleting a pointer twice.  Both of these actions can cause serious problems in a program.  For safety, build your functions to delete the memory they create.  Make them responsible parents. 

If you are writing a function that needs to create memory and then has to pass it back to the calling function, change your interface.  Instead, have the calling function allocate the memory, and then pass it into your function by reference.  The function can then manipulate the arguments and parameters and then pass it back to its parent method.  This moves all of the memory management out of your program, and back to the function that is prepared to delete it.

Remember:
1 - Pass parameters by value when you must, but by reference when you can.
2 - Return by value when you must.
3 - DON'T pass by reference if the item referred to may go out of scope.
4 - DON'T use references to null objects.


©2004 C. Germany