CHAPTER 13

User-Defined Types

Chapter Objectives

By the end of the chapter, readers will be able to:

images  Distinguish between primitive data types and user-defined types (UDTs).

images  Explain and use the typedef statement.

images  Explain, declare, and apply enumerated data types.

images  Define, discuss, and use structures.

images  Understand the value of unions and apply them in an application.

images  Explain the principles behind a memberwise copy.

Introduction

Up to this point, we've used either primitive data types, such as int, float, and char, or predefined types, such as ifstream and ofstream. This chapter is dedicated to the methodologies that allow us to create our own data types. For the first time you will be able to extend the C++ language rather than just using what is already available. As you will soon see, each of these user-defined types (UDT) has their own functionality, uses, and advantages. Using UDTs adds readability and another level of modularity to your code.

13.1 The typedef Statement

The reserved word typedef allows us to create an alternative name or synonym for an existing data type. Although this is the simplest of the methods discussed in this chapter, it is often used to simplify the name of more complicated data types. The syntax for a typedef is shown here:

typedef <data-type> <synonym>;

This is a very straightforward syntax, allowing for the simplification of many variable declarations. Several examples using typedef are shown in Example 13.1.1

images

After examining Example 13.1.1, we can see how typedef might make the use of long data types much easier and more readable. Once the synonym has been created, it can be used like any other data type, as demonstrated in Example 13.1.2.

images

Although the use of typedef can help simplify your code, it has a pretty significant drawback in that it could potentially hide too many details. Notice that the declaration of str in Example 13.1.2 hides the fact that str is a pointer; it would just seem odd to delete a variable that doesn't look like a pointer. To further demonstrate the confusion this can cause, examine Example 13.1.3.

images

Example 13.1.3 fully illustrates that using typedefs with pointers may add more confusion than extra value. Therefore, we suggest that care should be taken when using typedef with pointers.

STYLE NOTE Notice that the synonym examples used in this chapter are all uppercase, similar to constants. This is the generally accepted form for all typedef synonyms.

13.2 Enumerated Data Types

Another method of creating data types allows you to associate a list of identifiers with a type name. Any variable of this newly created type can have only the values you specified in the identifier list. This form of a user-defined type is common throughout many programming languages and is called an enumerated data type, or enum. The syntax for creating an enum is shown here:

enum <enum-name> {<identifier-list>};

The <enum-name> in the syntax is any user-specified name that conforms to C++ naming rules. The <identifier-list> is a comma-delimited list of finite values that will be considered legal for the data type indicated. Example 13.2.1 creates several data types using an enumerated data type.

images

Each identifier in the enum has an underlying integer value that, by default, starts at 0. Every subsequent identifier equates to the next value in sequential order. Therefore, in Example 13.2.1, the identifier OFF equates to 0, and ON equates to 1. These values correspond conceptually to what the identifier represents. In computers, 0 is often used to represent an “off” state, and 1 to represent an “on” state. The power switch on many electronic devices uses 0 and 1 to represent on and off. We can choose to either use the default values or change them, as shown in Example 13.2.2.

images

In Example 13.2.2, the CardRanks type specifies the identifier ACE to correspond with the value 1. All subsequent identifiers correspond to sequential values based on the last specified value, 1. You can also specify a value for each individual identifier, as shown in the Coins type.

Once the type is constructed, you can create variables just like you would with any other data type, as shown in Example 13.2.3.

images

It is possible to print any of these variables, although you are not going to get what you probably expect. Displaying an enum variable is shown in Example 13.2.4.

images

As you can see from Example 13.2.4, printing an enum identifier displays the underlying value, not the identifier itself. Although this capability of printing the identifier is not built into C++ as it is in some other languages, we can use a little ingenuity to display the appropriate text, as demonstrated in Example 13.2.5.

images

images

In Example 13.2.5, we create a constant array of string literals, with each literal matching a corresponding identifier in the enum. Then, instead of printing the enum identifier, we print the text from the array of strings using the identifier as the index into the ragged array.

Using enums adds some safety to your programs because of the extra type checking they provide. Only values specified in the enum declaration can be used in variables of that type. Although you can typecast an integer to be stored in a variable declared from an enum, you should always ensure that it is an appropriate value for that data type. Typecasting can potentially ruin any advantages gained by using an enum; in essence you are forcing the enum to comply with your value.

The best reason to use an enumerated data type is to increase the readability of your program. One last example of using enums is shown in Example 13.2.6.

images

images

In Example 13.2.6, the use of the enum adds a lot of readability to the switch statement. You no longer need to remember what each menu choice does because the enum identifier makes your source code much more self-documenting and maintainable.

STYLE NOTE The identifiers associated with an enumerated data type are usually created using all uppercase letters, as shown in this chapter.

Section 13.2 Exercises

1.  Associating a list of identifiers with a type name is called a(n) _______________.

a. UDT

b. typedef

c. enumerated data type

d. mistake

2.  Each identifier in a list of elements associated with an enum has a related integer value that by default begins at ____.

a. 0

b. 1

c. 2

d. unknown

3.  Create an enumerated UDT called Months. Populate it with the values of all the individual months in a year; then associate the first month with 1, the second month with 2, and so on.

4.  Declare a variable of type Months called turkey and initialize it to NOVEMBER.

5.  Explain whether or not the declaration of favorite_day in the following code could be used with this enum:

images

6.  Based on the declaration of days in the code in Exercise 5, what would be displayed by the following statement?

cout << WEDNESDAY;

7.  True or false: The best reason to use enums within your program is to help improve the overall performance of your application.

13.3 Structures

One of the most powerful means of creating a user-defined type is a structure. A structure allows you to encapsulate variables of any type into one UDT. Some languages call this concept a record because every variable of this type holds one record. The syntax for creating a structure is shown here:

struct <struct-name> { <data-members> };

The <data-members> in the syntax represents the individual variables that make up the structure. A structure definition is shown in Example 13.3.1.

images

In Example 13.3.1, each data member appears to be declared inside the structure. This is not entirely accurate, because until a variable is declared from the structure, no memory is allocated. The definition is the specification of the type, not the request for memory. The data members are merely placeholders until such time that memory is allocated (i.e., variable declaration). Therefore, it is illegal to try to provide an initial value for the data members within the structure definition.

Earlier we stated that you can have data members of any data type; this also includes pointers and other structures. Be careful, however, when using pointer data members though; when a structure variable is destroyed, the dynamic memory allocated to the pointer is not automatically deallocated. You must remember to free the memory before the variable goes out of scope or is destroyed in a delete statement.

13.3.1 Nested Structures

Nesting structures is useful for further extending the concept of code reuse. Although we could place the definition of a structure inside another structure, it is better to create two separate structure definitions so that you can reuse the individual structures as needed. The process of structure reuse is demonstrated in Example 13.3.2.

images

images

Remember, however, that the structure has to be defined before it is used. Therefore, in Example 13.3.2, Date is defined before it is used in Student, and Student is defined before it is used in Homework.

13.3.2 Structure Variables

Now that we understand how to define the structure itself, it's time to declare variables from the structure. By now you've probably guessed that any declaration form can be used. Variable declarations are shown in Example 13.3.3.

images

The final line of Example 13.3.3 demonstrates how we can initialize a structure variable. The values must be in the order in which the data members were specified in the structure definition. Although the nested curly braces are not necessary, they do give a more visual indication that we are initializing the values in the nested structure.

13.3.3 Accessing the Data Members

Once a variable has been declared, accessing each data member requires using the dot operator. We saw the dot operator when we discussed members of stream objects (i.e., cout.width). The dot operator specifies that the member on the right of the operator belongs to the structure variable on the left. Example 13.3.4 shows the use of the dot operator to access the data members of the variables declared in Example 13.3.3.

images

Example 13.3.4 demonstrates a couple of important issues related to accessing data members using the dot operator. First, don't forget the data type of the structure member. If the data member is a cString, for example, make sure to use the appropriate functions to manipulate the data. Second, notice the proper use of the subscript operator. It is necessary to specify the offset into the array of structures before referencing the data member.

One thing not shown in Example 13.3.4 is how to access the data members from a pointer. Example 13.3.5 shows an older, deprecated style of accessing data members using the dot operator.

images

The current standard has been around for decades and uses the arrow operator to access the data member. You have already seen the arrow operator in use when we discussed how to flush the input buffer in Chapter 10 (i.e., cin.rdbuf()->in_avail()). Example 13.3.6 converts Example 13.3.5 to the appropriate current style.

images

Nested structure data members are accessed just like other structure data members. Example 13.3.7 shows the appropriate procedure to access a nested data member, assuming the structure definition in Example 13.3.2.

images

13.3.4 Structure Variable Manipulation

An important concept to understand with structure variables is that they can be passed by any of the methods we have previously discussed: by value, by reference, or by pointer. This is true even if the structure definition contains an array data member. Normally arrays are always passed by pointer, but once they are encapsulated in a structure, they can, in essence, be passed by value.

Another operation we can perform on structure variables is assignment. There is no problem assigning a structure variable to another variable of the same type. C++ performs a member-wise copy that allows us to perform assignment operations.

You should be aware that you can't use cout to print an entire structure at once; you will have to print each member individually. There is a fairly advanced way around this problem, but it won't be covered in this text.

Another method of creating a UDT that's similar to a structure is called a union. In the following chapters, we will build on the concepts discussed in relation to structures to create an even more sophisticated method of creating UDTs.

STYLE NOTE

All of our examples specify that each data member of a structure be declared on a separate line. Syntactically you can have multiple member specifications on the same line, but it is considered poor style.

13.3.5 Shallow Copy Versus Deep Copy

Using pointers as data members within a structure can cause some problems because of the member-wise copy. If an assignment is made between two structure variables, the addresses in the pointers are copied. Therefore, two pointers will point to the same piece of memory. This is called a shallow copy. Examine the code in Example 13.3.8.

images

As shown in Example 13.3.8, memory is allocated and stored in the fname pointer. When the assignment of var1 into var2 is executed, two pointers will point to the same instance of “Mike”. This situation is illustrated in Figure 13.3.1.

When the code in Example 13.3.9 is executed, your program will crash.

images

Figure 13.3.1 Shallow copy

images

The crash will happen when the second statement in Example 13.3.9 is executed because the memory has already been deallocated. The solution to this problem is to create a deep copy. A deep copy allocates additional memory for the second variable's pointer and then copies the contents of fname into the newly allocated memory. This process is shown in Example 13.3.10.

images

images

images

Figure 13.3.2 Deep copy

The deep copy performed in the last two statements of Example 13.3.10 is illustrated in Figure 13.3.2.

As you can see from Figure 13.3.2, there will be no problems when one pointer data member is deallocated.

Section 13.3 Exercises

1.  Explain the value of a structure.

2.  Some programmers equate the idea of a structure with that of a ______________.

a. byte        b. field         c. record      d. file

3.  Using the structure that follows, please respond to each of the following questions:

images

images

a. How many data members are contained within this structure?

b. Declare a variable called club_president of type Student.

c. Write the necessary statements to create an array of 20 Students in a variable called

club_members.

d. Write the line of code to assign the gpa of the fourth student in your array the value 4.0.

4.  Explain what, if anything, is wrong with the following structure definition:

images

5.  Explain what, if anything, is wrong with the following structure definition:

images

6.  True or false: It is possible to pass a structure by value and by reference.

7.  True or false: The definition of a structure acts simply like a model or framework for its respective data members; the definition itself does not take up any memory.

8.  True or false: It is possible to have an array of structures and an array within a structure.

9.  True or false: Structures can contain other structures.

Section 13.3 Learn by Doing Exercise

1.  Write a program that has the following functionality:

a. Define a structure that has a character array for a person's first name, an integer for a person's age, and a character for a person's gender.

b. Create a data file that consists of five records based on the preceding information.

c. Create an array of structures and pass that array to a function called ReadData, where the array will be filled by reading from the data file.

d. Create a function called DisplayOne that is passed one element of the array by value and is then displayed to the screen.

e. Create a function called EditRef that is passed one element of the array by reference. The function will then allow the user to edit the information in the structure.

f. Create another function called EditPointer that has the same functionality as EditRef except that the structure is passed by pointer.

13.4 Unions

Another method of creating a UDT, called a union, is similar to a structure. When created, a union defines data members just like a structure. The difference is that a union will only store one value in memory, no matter how many members are specified. When a variable is created from a union, only enough memory is allocated to hold the largest data member. This is demonstrated in Example 13.4.1.

images

Since the structure SalesPay in Example 13.4.1 is the largest data type, 16 bytes, it is also the size of the union. Storing information into a union data member follows the same rules and conventions as a structure and is demonstrated in Example 13.4.2.

images

As you can see, there is no mystery in accessing the members of the union. However, there is a side effect. If a data member other than the one you used to store the data is accessed the results would be indeterminate. Example 13.4.3 demonstrates what happens when the wrong data member in a union is accessed.

images

As you can see from Example 13.4.3, the data doesn't make sense. However, this is not the worst of it. If you try to access the monthly_sales data member, you start to have real problems. Unfortunately, it depends on the compiler as to what happens. In Visual Studio, the warning “uninitialized local variable ‘pay’ used” is displayed by the compiler. Other compilers may show different warnings, or maybe even no warnings whatsoever. Because of this drawback, the use of a union is usually paired with a flag that helps the programmer determine which data member of the union was used. This technique is demonstrated in Example 13.4.4.

images

As you can see in Example 13.4.4, an enum is created to differentiate between the three different wage types designated by the union created in Example 13.4.1. We can now use this flag to perform the correct operations on the union's members, as shown in Example 13.4.5.

images

images

In Example 13.4.5, we determine the type of employee to be entered and then use a switch statement to enter the correct data depending on the specified type. As stated earlier, the pairing of a flag and a union is a common practice to help ensure the appropriate use of the union's data members.

STYLE NOTE Since all UDT definitions must appear prior to their use, they will typically be found directly after any #includes, in the global area of your program. This is because function declarations, which are usually global, require UDTs to specify the types of their parameters. There is a more standard way to include UDT definitions in your program, but this technique will not be discussed until Chapter 15.

Section 13.4 Exercises

1.  Assuming a union had four different data members, how many different values can it hold at any one time?

a. one        b. two        c. four        d. unknown

2.  Write the necessary statement to create a union called Sample to hold the following three data members: id (an integer), type (a single character), and size (a double).

3.  True or false: A union is similar to a structure, except that it can only have one data member.

4.  True or false: When creating a union, it is a good idea to also create a flag to help ensure the use of the union's correct data member.

13.5 Problem Solving Applied

This chapter introduced UDTs as a means of creating our own data types. The ability to construct data types is the basis for many of the applications that exist today. This section will walk you through an example designed to extend and apply what was just covered into a real-world application dealing with a couple different forms of media, namely books and audio books.

The problem we are trying to solve deals with how we could best manage a collection of different books, some of which are printed books (i.e., hardcover or paperback) and some of which are audio (i.e., CD or cassette tape).

We know that one of the first steps we must take is to determine how to organize our data in a logical fashion. Establishing the elements contained within our data object is crucial, as it will become the framework for holding, storing, and passing our information. A data object is the information that describes a specific entity. If it is logically organized, it should serve us well in the future in the areas of code reuse, readability, and maintainability.

We begin with the decision on which UDT to use in describing the data object. Data that is common among all instances of the data object should be stored in a structure. In essence, if the description states, “the book has a title and an author and an ISBN,” use a struct. If the description uses an or instead of an and—for example, “the book is either printed or audio”—use a union. Lastly, if there is a list of related items, such as genre types, use an enum.

Based on our initial review of the problem at hand, the structure describing an instance of a book object is presented here. This layout hopefully includes all the necessary components and information, organized in an efficient and effective manner.

images

images

13.6 C—The Differences

C has all of the capabilities to create user-defined types as outlined in this chapter. However, the use of the data types, once created, is a little different. Some versions of C don't allow the name of the data type to be used directly without specifying the form of UDT used to create the data type. This technique is required regardless of the type of UDT—enum, struct, or union—and is shown in Example 13.6.1.

images

Because of the extra work involved in having to specify the type of the UDT in C, typedefs are used much more heavily in C than in C++. Example 13.6.2 converts Example 13.6.1 to a more commonly used C style using a typedef.

images

As you can see in Example 13.6.2, using typedef allows us to simulate the functionality of C++ by letting us use the data type directly without having to specify the type of UDT.

13.7 SUMMARY

This chapter discussed the capability to create user-defined types (UDTs). This extremely powerful feature of C++ offers the opportunity to extend the language beyond the primitive data types provided.

We introduced the typedef statement, along with a corresponding discussion that examined how it can be used to help improve the overall readability of a program by allowing the programmer to create a shorter, or alternative, name for an existing data type.

Another popular option for creating our own types, called enumerated data types, was also presented. Enumerated data types allow us to specify a comma-delimited list of values that are legal for the specific type. By using enums, we help improve the overall readability of our program and add safety to our code by providing further type checking.

The discussion of enumerated data types was followed by an overview of structures. Without a doubt, structures provide the most popular and powerful option for creating user-defined types that we have seen so far. They allow us to easily encapsulate a number of different variables and data types into one package; help us simulate the appearance of a record; and provide another, easy to use, alternative for organizing and passing related information. One additional point made relating to structures involved a discussion of the characteristics associated with a shallow versus a deep copy. If pointers are contained within a structure, a deep copy needs to be used to allocate additional memory.

The final part of the chapter introduced another technique for creating a UDT called a union. While a union resembles a structure in that it can contain various data members of differing types, it can only store one value in memory at any given time. The memory required to store a union is an amount big enough to hold its largest data member. This ability to save space is one of the major advantages offered by unions.

13.8 Debugging Exercise

Download the following file from this book's website and run the program following the instructions noted in the code.

images

images

images

images

images

images

13.9 Programming Exercise

The following programming exercise is to be developed using all phases of the development method. Be sure to make use of good programming practices and style, such as constants, whitespace, indentation, naming conventions, and commenting. Make sure all input prompts are clear and descriptive and that your output is well formatted and looks professional.

For this exercise, you will be modifying a program that originally appeared as Programming Exercise 1 in Section 11.14. In the original exercise, you were asked to do the following:

Read from a file a person's name, Social Security number, wage, hours worked in a week, and status. If a person is a full-time employee, $5.00 is deducted from his or her wages for union fees. A person is full time if he or she has an “F” as the status. Time and a half is paid for any time worked over 40 hours in a week. For the output, you must display the person's name, Social Security number, wage, number of hours, straight time pay, and overtime pay; whether they are a part-time or full-time employee; and their net pay. Make sure the output is formatted into rows and columns and includes the appropriate titles and column headings.

Your job is to create a structure called Employee that contains the fields identified in the preceding paragraph. Use an array of structures to hold your data. Instead of having to pass all the parallel arrays to the various functions, you can simply pass your array of structures. The following is a copy of the data file used in the exercise in 11.14.

Data: Use the following information as data for your data file:

John Smith 123-09-8765 9.00 46 F

Molly Brown 432-89-7654 9.50 40 F

Tim Wheeler 239-34-3458 11.25 83 F

Keil Wader 762-84-6543 6.50 35 P

Trish Dish 798-65-9844 7.52 40 P

Anthony Lei 934-43-9843 9.50 56 F

Kevin Ashes 765-94-7343 4.50 30 P

Cheryl Prince 983-54-9000 4.65 45 F

Kim Cares 343-11-2222 10.00 52 F

Dave Cockroach 356-98-1236 5.75 48 F

Will Kusick 232-45-2322 15.00 45 P

13.10 Team Programming Exercise

After showing Marcus the convoluted structure created for this chapter's Problem Solving Applied, he immediately thought it would be great to have a program that used that structure. All the members of his family are avid readers, and he would like a program that allows him to keep track of all of their books.

Marcus said he would like to have “standard CRUD” operations available in his program. Unsure what he meant by CRUD, you asked Troy, who thought it was a type of billiards game. Thinking that Troy was wrong once again, you asked Sherry, who told you it was a database term that meant: Create, Retrieve, Update, and Delete. These operations are common among most database applications.

Your job is to write the program for Marcus.

13.11 Answers to Chapter Exercises

Section 13.2

1.  c. enumerated data type

2.  a. 0

3.  enum Months {JANUARY = 1, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER};

4.  Months turkey = NOVEMBER;

5.  No; favorite_day is trying to be initialized to the identifier PAYDAY, a value not contained within the list of items available.

6.  3

7.  False—the best reason is to improve readability.

Section 13.3

1.  A structure is a powerful UDT that is used to encapsulate variables of any type into a specific type created by the programmer.

2.  c. record

3.  a. 5

b. Student club_president;

c. Student club_members[20];

d. club_members[3].gpa = 4.0F;

4.  You cannot initialize a data member of a structure within its definition.

5.  A semicolon is required at the end of the definition.

6.  True, but you can also pass by pointer.

7.  True

8.  True

9.  True

Section 13.4

1.  a. one

2.  union Sample

images

3.  False

4.  True

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.114.19