| |
Main Menu | Next Section |
We have already noted the changes to the data members of the 'ListOfContracts'
class - we will use the variable name 'presentMax' in place of
'limit' and 'listOfContracts' will be a pointer to an array instead
of an array itself. This last change means we no longer need
a user defined data type for the array. Nor, do we need the constant
indicating the number of elements in the array. However, we decided
to use a constant to indicate by how much the array should grow
each time it gets full.
Of the three functions we designed algorithms for in the previous
sections, two of them, the constructor and 'Add', already exist
in the class declarations. True, we will need to modify their
code but the class declarations concern themselves only with what
not how. Therefore, since there are no changes in the receives
and returns for these functions, no changes need be made in their
declarations. We do need to declare the third function - the one
that will increase the size of the array. Note that this is another
one of those functions that only instances of the class need.
Like the already existing search function, users of this class
will not directly call this new function and therefore, like 'Search',
it should be declared in the private part of the class. Here is
the code:
// Class Declarations for ListOfContracts with the ability to add as many contracts
// as desired
// File: ch10lst1a.h (list3 because ch 8 had 1 and 2)
#include "contrct4.h" // Given a new name just because in chapter 10
const CONTRACT_GROWTH = 3; // Size to start list and to grow by when
// necessary
class ListOfContracts
{
public:
ListOfContracts();
/*
Purpose: To instantiate (create) an instance of the class 'ListOfContracts'.
Goal: A new instance of class 'ListOfContracts' now exists
Receives: NONE
RETURNS: NONE
*/
~ListOfContracts(); // the destructor - see discussion below
void Add(Contract contract);
/*
Purpose: To add an instance of the class 'Contract' to the list.
Goal: A new instance of class 'contract' is on the list
Receives: a contract
Returns: NONE:
This function is complex enough to deserve an algorithm:
1. Search to find if 'contract' already exists
2. If need to add contract (it is not in list)
2.1 Check to see if there is room on the list
If no room,
2.1.1 Create more room
2.2 Add contract
2.3. Increment numContract
Else
2.4 State that it already exists
*/
void Delete(int contractNum);
/*
Purpose: To delete an instance of the class 'Contract' from the list.
Goal: The instance of class 'contract' is deleted
Receives: A contract number
Returns: NONE:
*/
bool FindContract(int ID, Contract& contract);
/*
Purpose: To find out if a contract is in the list
Goal: If the list holds a contract with id 'ID', the function
returns that contract in the variable 'contract' and
the function returns true.
If not, the function returns false and 'contract' holds garbage.'
Receives: A contract ID
Returns: Whether or not the contract was found AND if found, the
contract itself.
*/
void ReplaceContract (Contract contract);
/*
Purpose: To replace the properties of one contract in the list
Goal: The properties of the contract in the list with the ID of
'contract' have been replaced
Receives: A contract
Returns: NONE
*/
void DisplayAll();
/*
Purpose: To display all the contracts in the list
Goal: All the contracts are displayed
Receives: NONE
Returns: NONE
*/
private:
int numContracts; // indicates next available slot in list
int presentMax; // present size of array
Contract* listOfContracts;
int Search(int contractID);
void IncreaseSize();
/* Another function worth an algorithm
1. Increment presentMax by CONTRACT_GROWTH
2. Get enough memory for array of new 'presentMax' size and have
'tempArray' point to it
3. Copy old list contents (listOfContracts) into new list area pointed to by
tempArray
4. Delete memory pointed to by listOfContracts
5. Assign pointer value in 'tempArray' to listOfContracts
*/
The code itself follows the discussion we just had. To review:
The function 'IncreaseSize is declared in the private section.
Note that it has no parameters and returns nothing. One might
question this because certainly 'IncreaseSize' needs the array
of contracts if it is going to change its size. However, 'IncreaseSize'
is part of the class. It, therefore, has direct access to the
data members of the instances of the class, including 'lisOfContracts'- the data member that points to the array.
Anytime the function member 'Add' is called, it will be associated with a specific instance of the class 'ListOfContracts. If 'Add' then calls 'IncreaseSize', 'IncreaseSize' will have access to the data members of the same instance, including 'listOfContracts'. Since it has access to these data members, it does not need to have them passed as parameters.
As with constructors, if we do not declare a destructor, the system
declares one for us. As long as we do not want anything special
to happen when an instance is destroyed, the default destructor
is just fine. This is usually the case when the data members of
the class do not include pointers. That is why we have not had
to worry about destructors up to now - our classes did not use
pointers.
However, there is a problem when one or more of the data members is a pointer. The default destructor returns the memory used by the pointer itself but NOT the memory pointed to by the pointer. Consider an instance of 'ListOfContracts'. Here is a picture of the memory involved in such an instance.

If the destructor for this instance is called, the memory used by 'numContracts', presentMax, and the pointer 'listOfContracts' would be returned to the system. However, the destructor knows nothing about the array being pointed to by 'listOfContracts' so its memory would not be returned. Here is how memory would look in such a situation. Note that the array is out there but there is no way to access it. This is an example of a memory leak.

Figure 10.6
memory would look as follows:

If the program later has the line:
memory will look as follows:

Note that the memory for the variable 'ptr' still exists but not
the memory it pointed to - the memory that used to hold the value
5. Note further that although the memory no longer exists (actually it is on the heap available to be used by another request), the memory location 'ptr' still acts as if it is pointing to it. Thus, if 'ptr' is used incorrectly, there could be problems.
Deleting the memory for an array is only slightly more complex.
Consider this modified version of the code we just saw:
int* ptr;
ptr = new int[4];
for (int j = 0; j < 4; j++)
{
ptr[j] = j*10;
}

Although the code says ptr points to an integer, we know that
'ptr' actually points to an array of integers. It is relatively
easy for the system to keep track of this fact and thus return
all the memory allocated. As long as we are dealing with an array
of integers, doubles, chars, bool, or some simple user defined type, there is no problem. However, there is a problem
when we have an array of some class type and that class type has
a non-default destructor. Now, that destructor needs to be called
separately for each element of the array.
For example, suppose Olympia wants a separate list of contracts
for each year she has been in business. We could implement this
with an array of type 'ListOfContracts'. Further, suppose that
we declare this array as a local variable in some function in
our program. When that function finishes, the array represented
by the local variable is destroyed and, in the process, the destructor
for 'ListOfContracts' must be called for each element of the array.
At different stages in its development, C++ has had different ways
of handling this. At first special code - indicating how many
elements there were in the array - was only required when the
array held instances of a class for which a specific destructor
had been defined. Other methodologies were proposed, and you would
not believe the amount of argument that surrounds details such
as this. In any
case, the latest approach and the one used in the latest compilers
says that any time a pointer points to an array, the programmer
should use 'delete' followed by a empty pair of brackets, followed
by the pointer. This tells the system that there is an array of
something to be deleted and it handles determining if a destructor
needs to be called and, if so, how often.
Thus to delete the memory accessed by 'ptr' above we would write:
Back to destructors: Since 'ListOfContracts' includes a pointer to an array of contracts, we need a destructor to delete the array. To declare a destructor for a class, one simply puts a tilde (~) in front of the class name. Like constructors, destructors cannot return anything and the declaration code does not even include the word 'void'. Also, destructors cannot receive anything so they have no parameters. The 'Contract' class, as presently designed, does not need an explicitly declared destructor but if we wanted one, we would declare it as:
Notice that what distinguishes a classes destructor from a constructor with no parameters is the tilde. To declare the destructor for ListOfContracts, we follow the same procedure and write:
Topics Covered in the "Essentials of C++"
| |
Main Menu | Next Section |