10.1 Structures

As we said in Chapter 6, an object is a variable that has member functions, and a class is a data type whose variables are objects. Thus, the definition of a class should be a data type definition that describes two things: (1) what kinds of values the variables can hold and (2) what the member functions are. We will approach class definitions in two steps. We will first tell you how to give a type definition for a structure. A structure (of the kind discussed here) can be thought of as an object without any member functions. After you learn about structures, it will be a natural extension to define classes.

Structures for Diverse Data

Sometimes it is useful to have a collection of values of different types and to treat the collection as a single item. For example, consider a bank certificate of deposit, which is often called a CD. A CD is a bank account that does not allow withdrawals for a specified number of months. A CD naturally has three pieces of data associated with it: the account balance, the interest rate for the account, and the term, which is the number of months until maturity. The first two items can be represented as values of type double, and the number of months can be represented as a value of type int. Display 10.1 shows the definition of a structure called CDAccount that can be used for this kind of account. The definition is embedded in a complete program that demonstrates this structure type definition. As you can see from the sample dialogue, this particular bank specializes in short-term CDs, so the term will always be 12 or fewer months. Let’s look at how this sample structure is defined and used.

Display 10.1 A Structure Definition

 1    //Program to demonstrate the CDAccount structure type.
 2    #include <iostream>
 3    using namespace std;
 4    //Structure for a bank certificate of deposit:
 5    struct CDAccount
 6    {
 7        double balance;
 8        double interestRate;
 9        int term; //months until maturity
10    };
11     
12     
13    void getData(CDAccount& theAccount);
14    //Postcondition: theAccount.balance and theAccount.interestRate
15    //have been given values that the user entered at the keyboard.
16     
17     
18    int main( )
19    {
20        CDAccount account;
21        getData(account);
22     
23        double rateFraction, interest;
24        rateFraction = account.interestRate / 100.0;
25        interest = account.balance * rateFraction * (account.term / 12.0);
26        account.balance = account.balance + interest;
27     
28        cout.setf(ios::fixed);
29        cout.setf(ios::showpoint);
30        cout.precision(2);
31        cout << "When your CD matures in "
32             << account.term << " months,\n"
33             << "it will have a balance of $"
34             << account.balance << endl;
35        return 0;
36    }
37     
38    //Uses iostream:
39    void getData(CDAccount& theAccount)
40    {
41        cout << "Enter account balance: $";
42        cin >> theAccount.balance;
43        cout << "Enter account interest rate: ";
44        cin >> theAccount.interestRate;
45        cout << "Enter the number of months until maturity\n"
46             << "(must be 12 or fewer months): ";
47        cin >> theAccount.term;
48    }

Sample Dialogue

Enter account balance: $100.00
Enter account interest rate: 10.0
Enter the number of months until maturity
(must be 12 or fewer months): 6
When your CD matures in 6 months,
it will have a balance of $105.00

The structure definition is as follows:

struct CDAccount
{
    double balance;
    double interestRate;
    int term; //months until maturity
};

The keyword struct announces that this is a structure type definition. The identifier CDAccount is the name of the structure type. The name of a structure type is called the structure tag. The tag can be any legal identifier (but not a keyword). Although this convention is not required by the C++ language, structure tags are usually spelled with a mix of uppercase and lowercase letters, beginning with an uppercase letter. The identifiers declared inside the braces, {}, are called member names. As illustrated in this example, a structure type definition ends with both a brace, }, and a semicolon.

A structure definition is usually placed outside of any function definition (in the same way that globally defined constant declarations are placed outside of all function definitions). The structure type is then available to all the code that follows the structure definition.

Once a structure type definition has been given, the structure type can be used just like the predefined types int, char, and so forth. For example, the following will declare two variables, named myAccount and yourAccount, both of type CDAccount:

CDAccount myAccount, yourAccount;

A structure variable can hold values just like any other variable can hold values. A structure value is a collection of smaller values called member values. There is one member value for each member name declared in the structure definition. For example, a value of the type CDAccount is a collection of three member values: two of type double and one of type int. The member values that together make up the structure value are stored in member variables, which we discuss next.

Each structure type specifies a list of member names. In Display 10.1 the structure CDAccount has the three member names balance, interestRate, and term. Each of these member names can be used to pick out one smaller variable that is a part of the larger structure variable. These smaller variables are called member variables. Member variables are specified by giving the name of the structure variable followed by a dot (that is, followed by a period) and then the member name. For example, if account is a structure variable of type CDAccount (as declared in Display 10.1), then the structure variable account has the following three member variables:

account.balance
account.interestRate
account.term

The first two member variables are of type double, and the last is of type int. These member variables can be used just like any other variables of those types. For example, the member variables above can be given values with the following three assignment statements:

account.balance = 1000.00;
account.interestRate = 4.7;
account.term = 11;

The result of these three statements is diagrammed in Display 10.2. Member variables can be used in all the ways that ordinary variables can be used. For example, the following line from the program in Display 10.1 will add the value contained in the member variable account.balance and the value contained in the ordinary variable interest and will then place the result in the member variable account.balance:

Display 10.2 Member Values

An illustration shows a code segment and the allocation of the “Member Values.”
account.balance = account.balance + interest;

Notice that you specify a member variable for a structure variable by using the dot operator in the same way you used it in Chapter 6, where the dot operator was used to specify a member function of a class. The only difference is that in the case of structures, the members are variables rather than functions.

Two or more structure types may use the same member names. For example, it is perfectly legal to have the following two type definitions in the same program:

struct FertilizerStock
{
    double quantity;
    double nitrogenContent;
};

and

struct CropYield
{
    int quantity;
    double size;
};

This coincidence of names will produce no problems. For example, if you declare the following two structure variables:

FertilizerStock superGrow;
CropYield apples;

then the quantity of superGrow fertilizer is stored in the member variable superGrow.quantity and the quantity of apples produced is stored in the member variable apples.quantity. The dot operator and the structure variable specify which quantity is meant in each instance.

A structure value can be viewed as a collection of member values. Viewed this way, a structure value is many different values. A structure value can also be viewed as a single (complex) value (which just happens to be made up of member values). Since a structure value can be viewed as a single value, structure values and structure variables can be used in the same ways that you use simple values and simple variables of the predefined types such as int. In particular, you can assign structure values using the equal sign. For example, if apples and oranges are structure variables of the type CropYield defined earlier, then the following is perfectly legal:

apples = oranges;

This assignment statement is equivalent to:

apples.quantity = oranges.quantity;
apples.size = oranges.size;

When we assign a structure variable in this way we are performing a Shallow copy. This means that the individual member variables are directly copied. This works fine for simple variables, but we will see later that this can cause problems when variables are dynamically allocated.

Structures as Function Arguments

A function can have call-by-value parameters of a structure type and/or call-by-reference parameters of a structure type. The program in Display 10.1, for example, includes a function named getData that has a call-by-reference parameter with the structure type CDAccount.

A structure type can also be the type for the value returned by a function. For example, the following defines a function that takes three appropriate arguments and returns a value of type CDAccount:

CDAccount shrinkWrap(double theBalance,
                     double theRate, int theTerm)
{
    CDAccount temp;
    temp.balance = theBalance;
    temp.interestRate = theRate;
    temp.term = theTerm;
    return temp;
}

Notice the local variable temp of type CDAccount; temp is used to build up a complete structure value, which is then returned by the function. Once you have defined the function shrinkWrap, you can give a value to a variable of type CDAccount as illustrated by the following:

CDAccount newAccount;
newAccount = shrinkWrap(10000.00, 5.1, 11);

Programming Tip Use Hierarchical Structures

Sometimes it makes sense to have structures whose members are themselves smaller structures. For example, a structure type called PersonInfo, which can be used to store a person’s height, weight, and birth date, can be defined as follows:

struct Date
{
     int month;
     int day;
     int year;
};
struct PersonInfo
{
    double height; //in inches
    int weight; //in pounds
    Date birthday;
};

A structure variable of type PersonInfo is declared in the usual way:

PersonInfo person1;

If the structure variable person1 has had its value set to record a person’s birth date, then the year the person was born can be output to the screen as follows:

cout << person1.birthday.year;

The way to read such expressions is left to right, and very carefully. Starting at the left end, person1 is a structure variable of type PersonInfo. To obtain the member variable with the name birthday, use the dot operator as follows:

person1.birthday

This member variable is itself a structure variable of type Date. Thus, this member variable has member variables itself. A member variable of the structure variable person1.birthday is obtained by adding a dot and the member variable name, such as year, which produces the expression person1. birthday.year shown previously.

Initializing Structures

You can initialize a structure at the time that it is declared. To give a structure variable a value, you follow it by an equal sign and a list of the member values enclosed in braces. For example, the following definition of a structure type for a date was given in the previous subsection:

struct Date
{
    int month;
    int day;
    int year;
};

Once the type Date is defined, you can declare and initialize a structure variable called dueDate as follows:

Date dueDate = {12, 31, 2004};

Be sure to notice that the initializing values must be given in the order that corresponds to the order of member variables in the structure type definition. In this example, dueDate.month receives the first initializing value of 12, dueDate.day receives the second value of 31, and dueDate.year receives the third value of 2004.

It is an error if there are more initializers than struct members. If there are fewer initializer values than struct members, the provided values are used to initialize data members, in order. Each data member without an initializer is initialized to a zero value of an appropriate type for the variable.

Self-Test Exercises

  1. Given the following structure and structure variable declaration:

    struct TermAccount
    {
        double balance;
        double interestRate;
        int term;
        char initial1;
        char initial2;
    };
    TermAccount account;

    what is the type of each of the following? Mark any that are not correct.

    1. account.balance

    2. account.interestRate

    3. TermAccount.term

    4. savingsAccount.initial1

    5. account.initial2

    6. account

  2. Consider the following type definition:

    struct ShoeType
    {
        char style;
        double price;
    };

    Given this structure type definition, what will be the output produced by the following code?

    ShoeType shoe1, shoe2;
    shoe1.style ='A';
    shoe1.price = 9.99;
    cout << shoe1.style << " $" << shoe1.price << endl;
    shoe2 = shoe1;
    
    shoe2.price = shoe2.price/9;
    cout << shoe2.style << " $" << shoe2.price << endl;
  3. What is the error in the following structure definition? What is the message your compiler gives for this error? State what the error is, in your own words.

    struct Stuff
    {
        int b;
        int c;
    }
    int main( )
    {
        Stuff x;
        //other code
    }
  4. Given the following struct definition:

    struct A
    {
        int memberB;
        int memberC;
    };

    declare x to have this structure type. Initialize the members of x, memberB and memberC, to the values 1 and 2, respectively.

    (Note: This requests an initialization, not an assignment of the members. This distinction is important and will be made in a later chapter.)

  5. Here is an initialization of a structure type. Tell what happens with each initialization. Note any problems with these initializations.

    struct Date
    {
        int month;
        int day;
        int year;
    };
    1. Date dueDate = {12, 21};

    2. Date dueDate = {12, 21, 20, 22};

    3. Date dueDate = {12, 21, 20, 22};

    4. Date dueDate = {12, 21, 22};

  6. Write a definition for a structure type for records consisting of a person’s wage rate, accrued vacation (which is some whole number of days), and status (which is either hourly or salaried). Represent the status as one of the two char values 'H' and 'S'. Call the type EmployeeRecord.

  7. Give a function definition corresponding to the following function declaration. (The type ShoeType is given in Self-Test Exercise 2.)

    void readShoeRecord(ShoeType& newShoe);
    //Fills newShoe with values read from the keyboard.
  8. Give a function definition corresponding to the following function declaration. (The type ShoeType is given in Self-Test Exercise 2.)

    ShoeType discount(ShoeType oldRecord);
    //Returns a structure that is the same as its argument,
    //but with the price reduced by 10%.
  9. Give the structure definition for a type named StockRecord that has two member variables, one named shoeInfo of the type ShoeType given in Self-Test Exercise 2 and one named arrivalDate of type Date given in Self-Test Exercise 5.

  10. Declare a variable of type StockRecord (given in the previous exercise) and write a statement that will set the year of the arrival date to 2006.