Part B - Foundations

Compound Types II

Define an array of instances of a compound data type
Define a compound data type that contains another compound data type
Outline a walkthrough technique for programs that include compound data types

"Much computing involves creating collections of various forms of objects and then manipulating such collections. Reading characters into a string and printing out the string is a simple example. A class with the main purpose of holding objects is commonly called a container" (Stroustrup, 1997)

Arrays | Containers | Walkthrough | In-Class Exercise | Summary

We can create complex compound types from simpler ones in the same way that we create simple compound types from primitive types.  We can define an array of instances of a compound type just like we define an array of elements of primitive type.  We can also include a compound type as a data member in a more complex type.

In this chapter, we describe the syntax for creating and accessing arrays of instances of compound types along with the syntax for creating and accessing compound types that contain other compound types.  We conclude by presenting a technique for walking through code that includes instances of a compound type.

Arrays

To declare an array of instances of a compound type, we suffix the instance identifier with a brackets-enclosed number.  This number, which is an integer constant or integer-constant expression, specifies the number of elements in the array.

For example, to define an array of 40 Students, we write:

 ``` struct Student student; ```

The rules that apply to arrays of primitive types apply equally to arrays of compound types:

• element indexing is 0-based - the first element is student
• student[i] refers to the i+1-th element of the array
• the name of the array alone refers to the address of its start - student is the same as &student

Alternative to Parallel Arrays

Parallel Arrays

Parallel arrays are a traditional technique for organizing data in tabular form.  For instance, we store the student numbers and grades of 40 students in two separate arrays

 ``` int no; char grade; ```

To initialize the first three elements of these two parallel arrays, we write:

 ``` int no = {10001, 10002, 10003}; char grade = {"CAC", "CBB", "AAB"}; ```

The number of and the grades for the third student are given by no and grade respectively.  The common index is a defining feature of parallel arrays.

Array of Instances

An array of instances of a compound data type provides a simple alternative to parallel arrays.  Each instance holds the data for an entire row in the table.  The members of each instance hold the field data for a row.  Instead of defining two parallel arrays, we write:

 ``` struct Student { int no; char grade; }; struct Student student; ```

The number of and the grades for the third student are given by student.no and student.grade respectively.

To initialize the members of the first three elements of the array, we write:

 ``` struct Student student = {{10001,"CAC"},{10002,"CBB"},{10003,"AAB"}}; ```

We arrange the data values instance by instance and the interior braces distinguish the data values for one instance from those for another instance.  The interior braces are optional and we may simply list the values in the order in which they are stored in memory

 ``` struct Student student = {10001, "CAC", 10002, "CBB", 10003, "AAB"}; ```

Passing an Element of an Array of Instances

If we pass a single element of an array to a function, the function receives a copy of that element and stores it in a separate instance.  Within the function, we refer to the members of the copied instance and not the original element:

 ``` // Student.h struct Student { int no; char grade; }; // main.cpp #include using namespace std; #include "Student.h" void display(const struct Student*); int main() { int i; struct Student student = {10001,"CAC",10002,"CBB",10003,"AAB"}; for (i = 0; i < 3; i++) display(&student[i]); } void display(const struct Student* s) { cout << s->no << ' ' << s->grade << endl; } ```

Containers

We call a compound type that holds one or more other compound types a container type.  Defining a container type allow us to include other types without describing their implementation details.  We describe their implementation details outside the container type.  Because we describe the included type and the container type separately, we can update them independently.

We call a compound type that contains another compound type throughout the container's lifetime a composition

One example of a container type is a system box, which contains a motherboard, which contains a CPU, which contains an ALU.  Another example is a section of a course, which contains a list of enrolled students.  For this case, we define a Section type that contains an array of Student types:

 ``` struct Student { int no; char grade; }; struct Section { int noOfStudents; struct Student student; }; ```

Note that a compound type may NOT contain an instance of its itself.

 ``` struct Section { int noOfStudents; struct Section section; /* ERROR */ }; ```

Member Access

Dot, arrow, and subscript syntax applies to compositions as well as to simple compound types.  To define an instance of a Section, we write:

 ``` struct Section abc123a; ```

To set the number of students in abc123a to 23, we write:

 ``` abc123a.noOfStudents = 23; ```

To set the student number of the sixth student to 123-456-789, we write:

 ``` abc123a.student.no = 123456789; ```

To set the third grade of the sixth student to an 'A', we write:

 ``` abc123a.student.grade = 'A'; ```

We say that abc123a has a student with student number 123456789, whose third grade is 'A'

Walkthrough

Walking through an application that includes instances of compound data types requires some care in the preparation of the walkthrough table(s).  The table for the following program is shown below.

 ``` #include using namespace std; struct A { int x; double r; }; void foo(struct A* c); struct A goo(struct A d); int main( ) { struct A a = {4, 6.67}, b; foo(&a); cout << "00" << a.x << '.' << a.r << ".111" << endl; b = goo(a); cout << "00" << a.x << '.' << a.r << ".112" << endl; cout << b.x << '.' << b.r << ".113" << endl; } void foo(struct A* c) { int i; i = c->x; c->x = c->r; c->r = c->x % i + 202.134; } struct A goo(struct A d) { struct A e; d.x = d.r - 62; e = d; return e; } ```

The table includes:

• the return type for each function
• the name of each function
• the data type of each instance
• the name of each instance
• the data type of each member or variable
• the name of each member or variable

We reserves a separate line for the addresses of the instances and variables that are pointed to:

 int void struct A main foo goo struct A struct A struct A* struct A struct A a b c d e 1000 100C 1018 101C 1020 102C int double int double int int double int double x r x r i x r x r 1000 1000 1000 1000 1000

Output:

In-Class Exercise

Complete the Handout on Walkthrough on A Compound Type

Summary

• to allocate memory for an array of instances of a compound type use the same bracket notation as with primitive types
• to access an element in an array of instances of a compound type use the same subscript notation as with primitive types
• to access a data member of an element in an array of instances of a compound type follow the bracket notation with the dot operator and the name of the data member
• a compound type may contain an instance of another compound type as one of its data members
• a container type is a compound type that contains an instance of another compound type as one of its data members
• to access an instance of a compound type within an instance of a container type, use the dot operator followed by the name of the instance that is within the container type
• to access a data member of an instance of a compound type within an instance of a container type, use the dot operator followed by the name of the instance within the container type, followed by the dot operator and the name of the data member

 Designed by Chris Szalwinski Copying From This Site  