11.3 Arrays and Classes

You can combine arrays, structures, and classes to form intricately structured types such as arrays of structures, arrays of classes, and classes with arrays as member variables. In this section we discuss a few simple examples to give you an idea of the possibilities.

Arrays of Classes

The base type of an array may be any type, including types that you define, such as structure and class types. If you want each indexed variable to contain items of different types, make the array an array of structures. For example, suppose you want an array to hold ten weather data points, where each data point is a wind velocity and a wind direction (north, south, east, or west). You might use the following type definition and array declaration:

struct WindInfo
{
    double velocity; //in miles per hour
    char direction; //'N', 'S', 'E', or 'W'
};
WindInfo dataPoint[10];

To fill the array dataPoint, you could use the following for loop:

int i;
for (i = 0; i < 10; i++)
{
    cout << "Enter velocity for "
         << i << " numbered data point: ";
    cin >> dataPoint[i].velocity;
    cout << "Enter direction for that data point"
         << " (N, S, E, or W): ";
    cin >> dataPoint[i].direction;
}

The way to read an expression such as dataPoint[i].velocity is left to right and very carefully. First, dataPoint is an array. So, dataPoint[i] is the ith indexed variable of this array. An indexed variable of this array is of type WindInfo, which is a structure with two member variables named velocity and direction. So, dataPoint[i].velocity is the member variable named velocity for the ith array element. Less formally, dataPoint[i].velocity is the wind velocity for the ith data point. Similarly, dataPoint[i].direction is the wind direction for the ith data point.

The ten data points in the array dataPoint can be written to the screen with the following for loop:

for (i = 0; i < 10; i++)
    cout << "Wind data point number " << i << ": \n"
         << dataPoint[i].velocity
         << " miles per hour\n"
         << "direction " << dataPoint[i].direction
         << endl;

Display 11.9 contains the definition for a class called Money. Objects of the class Money are used to represent amounts of money in U.S. currency. The definitions of the member functions, member operations, and friend functions for this class can be found in Displays 11.3 through 11.8 and in the answer to Self-Test Exercise 13. You can have arrays whose base type is the type Money. A simple example is given in Display 11.9. That program reads in a list of five amounts of money and computes how much each amount differs from the largest of the five amounts. Notice that an array whose base type is a class is treated basically the same as any other array. In fact, the program in Display 11.9 is very similar to the program in Display 7.1, except that in Display 11.9 the base type is a class.

Display 11.9 Program Using an Array of Money Objects

 1    //This is the definition for the class Money.
 2    //Values of this type are amounts of money in U.S. currency.
 3    #include <iostream>
 4    using namespace std;
 5    class Money
 6    {
 7    public:
 8    friend Money operator +(const Money& amount1, const Money& amount2);
 9    //Returns the sum of the values of amount1 and amount2.
10    friend Money operator −(const Money& amount1, const Money& amount2);
11    //Returns amount1 minus amount2.
12    friend Money operator −(const Money& amount);
13    //Returns the negative of the value of amount.
14    friend bool operator ==(const Money& amount1, const Money& amount2);
15    //Returns true if amount1 and amount2 have the same value; false otherwise.
16    friend bool operator <(const Money& amount1, const Money& amount2);
17    //Returns true if amount1 is less than amount2; false otherwise.
18    Money(long dollars, int cents);
19    //Initializes the object so its value represents an amount with
20    //the dollars and cents given by the arguments. If the amount
21    //is negative, then both dollars and cents should be negative.
22    Money(long dollars);
23    //Initializes the object so its value represents $dollars.00.
24    Money( );
25    //Initializes the object so its value represents $0.00.
26    double getValue( ) const;
27    //Returns the amount of money recorded in the data portion of the calling
28    //object.
29    friend istream& operator >>(istream& ins, Money& amount);
30    //Overloads the >> operator so it can be used to input values of type
31    //Money. Notation for inputting negative amounts is as in − $100.00.
32    //Precondition: If ins is a file input stream, then ins has already been
33    //connected to a file.
34     
35    friend ostream&  operator <<(ostream& outs, const Money& amount);
36    //Overloads the << operator so it can be used to output values of type
37    //Money. Precedes each output value of type Money with a dollar sign.
38    //Precondition: If outs is a file output stream, then outs has already been
39    //connected to a file.
40    private:
41        long allCents;
42    };
43
<The definitions of the member functions and the overloaded operators goes here.>
44    //Reads in 5 amounts of money and shows how much each
45    //amount differs from the largest amount.
46    int main( )
47    {
48        Money amount[5], max;
49        int i;
50        cout << "Enter 5 amounts of money:\n";
51        cin >> amount[0];
52        max = amount[0];
53        for (i = 1; i < 5; i++)
54        {
55            cin >> amount[i];
56            if (max < amount[i])
57                max = amount[i];
58            //max is the largest of amount[0], . . ., amount[i].
59        }
60        Money difference[5];
61        for (i = 0; i < 5; i++)
62            difference[i] = max − amount[i];
63        cout << "The highest amount is " << max << endl;
64        cout << "The amounts and their\n"
65             << "differences from the largest are:\n";
66        for (i = 0; i < 5; i++)
67        {
68            cout << amount[i] << " off by "
69                 << difference[i] << endl;
70        }
71        return 0;
72    }

Sample Dialogue

Enter 5 amounts of money:
$5.00 $10.00 $19.99 $20.00 $12.79
The highest amount is $20.00
The amounts and their
differences from the largest are:
$5.00 off by $15.00
$10.00 off by $10.00
$19.99 off by $0.01
$20.00 off by $0.00
$12.79 off by $7.21

When an array of classes is declared, the default constructor is called to initialize the indexed variables, so it is important to have a default constructor for any class that will be the base type of an array. An array of classes is manipulated just like an array with a simple base type like int or double. For example, the difference between each amount and the largest amount is stored in an array named difference, as follows:

Money difference[5];
for (i = 0; i < 5; i++)
    difference[i] = max − amount[i];

Self-Test Exercises

  1. Give a type definition for a structure called Score that has two member variables called homeTeam and opponent. Both member variables are of type int. Declare an array called game that is an array with ten elements of type Score. The array game might be used to record the scores of each of ten games for a sports team.

  2. Write a program that reads in five amounts of money, doubles each amount, and then writes out the doubled values to the screen. Use one array with Money as the base type. (Hint: Use Display 11.9 as a guide, but this program will be simpler than the one in Display 11.9.)

Arrays as Class Members

You can have a structure or class that has an array as a member variable. For example, suppose you are a speed swimmer and want a program to keep track of your practice times for various distances. You can use the structure myBest (of the type Data given next) to record a distance (in meters) and the times (in seconds) for each of ten practice tries swimming that distance:

struct Data
{
     double time[10];
     int distance;
};
Data myBest;

The structure myBest, declared above, has two member variables: One, named distance, is a variable of type int (to record a distance); the other, named time, is an array of ten values of type double (to hold times for ten practice tries at the specified distance). To set the distance equal to 20 (meters), you can use the following:

myBest.distance = 20;

You can set the ten array elements with values from the keyboard as follows:

cout << "Enter ten times (in seconds):\n";
for (int i = 0; i < 10; i++)
    cin >> myBest.time[i];

The expression myBest.time[i] is read left to right: myBest is a structure; myBest.time is the member variable named time. Since myBest.time is an array, it makes sense to add an index. So, the expression myBest.time[i] is the ith indexed variable of the array myBest.time. If you use a class rather than a structure type, then you can do all your array manipulations with member functions and avoid such confusing expressions. This is illustrated in the following Programming Example.

Programming Example A Class for a Partially Filled Array

Display 11.10 shows the definition for a class called TemperatureList, whose objects are lists of temperatures. You might use an object of type TemperatureList in a program that does weather analysis. The list of temperatures is kept in the member variable list, which is an array. Since this array will typically be only partially filled, a second member variable, called size, is used to keep track of how much of the array is used. The value of size is the number of indexed variables of the array list that are being used to store values.

Display 11.10 Program for a Class with an Array Member

 1    //This is a definition for the class
 2    //TemperatureList. Values of this type are lists of Fahrenheit temperatures.
 3     
 4    #include <iostream>
 5    #include <cstdlib>
 6    using namespace std;
 7     
 8    const int MAX_LIST_SIZE = 50;
 9     
10    class TemperatureList
11    {
12    public:
13        TemperatureList( );
14        //Initializes the object to an empty list.
15     
16        void addTemperature(double temperature);
17        //Precondition: The list is not full.
18        //Postcondition: The temperature has been added to the list.
19     
20        bool full( ) const;
21        //Returns true if the list is full; false otherwise.
22     
23        friend ostream& operator <<(ostream& outs,
24            const TemperatureList& theObject);
25        //Overloads the << operator so it can be used to output values of
26        //type TemperatureList. Temperatures are output one per line.
27        //Precondition: If outs is a file output stream, then outs
28        //has already been connected to a file.
29    private:
30        double list[MAX_LIST_SIZE]; //of temperatures in Fahrenheit
31        int size; //number of array positions filled
32    };
33     
34    //This is the implementation for the class TemperatureList.
35     
36    TemperatureList::TemperatureList( ) : size(0)
37    {
38        //Body intentionally empty.
39    }
40    void TemperatureList::addTemperature(double temperature)
41    {//Uses iostream and cstdlib:
42        if ( full( ) )
43        {
44            cout << "Error: adding to a full list.\n";
45            exit(1);
46        }
47        else
48        {
49            list[size] = temperature;
50            size = size + 1;
51        }
52    }
53    bool TemperatureList::full( ) const
54    {
55        return (size == MAX_LIST_SIZE);
56    }
57    //Uses iostream:
58    ostream& operator <<(ostream& outs, const TemperatureList& theObject)
59    {
60        for (int i = 0; i < theObject.size; i++)
61            outs << theObject.list[i] << " F\n";
62        return outs;
63    }

An object of type TemperatureList is declared like an object of any other type. For example, the following declares myData to be an object of type TemperatureList:

TemperatureList myData;

This declaration calls the default constructor with the new object myData, and so the object myData is initialized so that the member variable size has the value 0, indicating an empty list.

Once you have declared an object such as myData, you can add an item to the list of temperatures (that is, to the member array list) with a call to the member function addTemperature as follows:

myData.addTemperature(77);

In fact, this is the only way you can add a temperature to the list myData, since the array list is a private member variable. Notice that when you add an item with a call to the member function addTemperature, the function call first tests to see if the array list is full and adds the value only if the array is not full.

The class TemperatureList is very specialized. The only things you can do with an object of the class TemperatureList are to initialize the list so it is empty, add items to the list, check if the list is full, and output the list. To output the temperatures stored in the object myData (declared previously), the call would be as follows:

cout << myData;

With the class TemperatureList you cannot delete a temperature from the list (array) of temperatures. You can, however, erase the entire list and start over with an empty list by calling the default constructor, as follows:

myData = TemperatureList();

The type TemperatureList uses almost no properties of temperatures. You could define a similar class for lists of pressures or lists of distances or lists of any other data expressed as values of type double. To save yourself the trouble of defining all these different classes, you could define a single class that represents an arbitrary list of values of type double without specifying what the values represent.

Self-Test Exercises

  1. Change the class TemperatureList given in Display 11.10 by adding a member function called getSize, which takes no arguments and returns the number of temperatures on the list.

  2. Change the type TemperatureList given in Display 11.10 by adding a member function called getTemperature, which takes one int argument that is an integer greater than or equal to 0 and strictly less than MAX_LIST_SIZE. The function returns a value of type double, which is the temperature in that position on the list. So, with an argument of 0, getTemperature returns the first temperature; with an argument of 1, it returns the second temperature, and so forth. Assume that getTemperature will not be called with an argument that specifies a location on the list that does not currently contain a temperature.