Lab Manual C++ Chapter2
User Manual:
Open the PDF directly: View PDF .
Page Count: 26
Download | ![]() |
Open PDF In Browser | View PDF |
Lab 2.1 Exploring Addressing Project 2.1 Binary Representation Lab 2.2 Create Your Own Types—Enumerations Project 2.2 Rock Operations NOTES TO THE INSTRUCTOR The first three lab exercises (1.1, 2.1, and 2.2) and projects (2.1 and 2.2) are intended for use in the first week(s) of the course. Any of them can be assigned as first labs and projects; it depends on your preferences for starting the course. Lab Exercises 2.1 and 2.2 are intended for use with Chapter 2: “Introduction to Data Types” of the text ADT,. Data Structures, and Problem Solving in C++, 2E. Lab Exercise 2.1 reviews the fundamental C++ data types and their memory requirements as discussed in Section 2.2: “C++’s Simple Data Types.” It also serves as an introduction to address arithmetic, discussed in Section 2.4: “Pointers.” The intent in Project 1.2 is to have students review the basic ideas and programming techniques they acquired in a first programming course while at the same time learning something new—namely, C++’s bitwise operations: &, |, ^ , ~, <<, and >>. These typically are not covered in a first course, but it is important that students know about them, even if only to understand why boolean expressions such as (x < 4) & (x > 1 ) or “malformed” output expressions like x << 3 do not cause compiler errors but probably do not execute in the manner intended. If the students come into this course knowing C++, these should be about the only new C++ features they encounter in doing the project. If they come to this course from Java, the C++ statements used in this program (except for input and output) are basically the same as in Java. Appendix E: “From Java to C++” in the text has proved useful in helping these students make the transition in a matter of a week or so. All of the students should be informed that Appendix C: “Basic C++” provides a ready reference for the essentials of C++, and that Appendix D: “Other C++ Features” covers some of the more advanced features. Notes: You might use the following modern-day version of the Lewis Carroll quote at the beginning of the lab exercise to illustrate the relationship between a variable, its name, its memory location, and its value: “The student’s middle initial is called charVar." “Oh, that’s her middle initial, is it?” Alice said, trying to feel interested. “No, you don’t understand," the Knight said, looking a little vexed. “That’s what it’s called. charVar is the name of a variable whose value is her middle initial.” “Then I ought to have said The student’s middle initial is a variable’?” Alice corrected herself. “No, you oughtn’t: that’s quite another thing! The variable is memory location 0x1224: but that’s only what it’s called, you know!” “Well, what is her middle initial, then?" said Alice, who was by this time completely bewildered. “I was coming to that," the Knight said. “Her middle initial really is ‘A’ and it’s my own initialization." Lab Exercise 2.2 uses the file g e o l o g y .cpp, which can be downloaded from the website whose URL is given in the preface to this lab manual. 9 f o Lab 2.1: Exploring Addressing Course Info:_____________ Name: Lab 2.1 Exploring Addressing “The name of the song is called 'Haddocks’ Eyes’.” “Oh, that’s the name of the song, is it?” Alice said, trying to feel interested. “No, you don’t understand,” the Knight said, looking a little vexed. "That’s what the name is called. The name really is The Aged Aged Man’.” “Then I ought to have said, ‘That’s what the song is called ’?’” Alice corrected herself. “No, you oughtn’t: that’s quite another thing! The song is called 'Ways and Means’: but that’s only what it’s called, you know!” “Well, what is the song, then?” said Alice, who was by this time completely bewildered. “I was coming to that,” the Knight said. “The song really is ‘A-Sitting On a Gate’: and the tune’s my own invention.” Objective: Lab 1.2 provides a review of C++’s fundamental data types and their memory requirements. These are discussed in Section 2.2: “C++’s Simple Data Types” starting on page 46 of the text ADTs, Data Structures, and Problem Solving in C++, 2E. Address arithmetic, address translation, and pointers (memory references) are introduced as described in Section 2.4: “Pointers.” Exploring addressing will provide insight into the way that memory is allocated and provide experience in the concept of memory maps and the memory requirements of C++’s basic built-in data types. Views of a Data Item A data item used as a variable has four aspects: 1) It has a name, which is what the programmer will call the data item and how the compiler will keep track of it. The name is generally assigned using a declaration statement. 2) It has an address of a location in memory that is allocated to it and where the item’s value will be stored. This address can be statically determined at compile time or dynamically assigned at run time. The association of a name and an address is called binding. 3) It has a type, which specifies what kinds of values it may have and thus determines how much space it will require in memory due to the number of bits (binary dig its) required to represent the value. Memory is usually measured in units called bytes (a byte is 8 bits). The size of a data type is the number of bytes required to store it. For the built-in basic data types, this size is fixed. Programmer-defined types can combine groups of both basic types and other programmer-defined types, which means that the size of programmer-designed data types can vary widely. 4) It has a value, which is a meaningful set of bits that are stored in the allocated memory location(s). Until now you have probably not concerned yourself with how much memory your data uses or what your data’s memory addresses are. In this lab exercise we are going to learn how to find the addresses of variables and how to find the sizes of the various data types. 11 Lab 2.1: Exploring Addressing Finding Addresses 1 You will begin by building the shell of a test program a d d r e s s . c p p that will be the beginning of your address laboratory. / * ----- a d d r e s s . c p p --------------------------------------------------------------------------------------------P ro g ra m t o e x p l o r e a d d r e s s i n g . Nam e: D a te : O th e r i n f o r m a t i o n r e q u i r e d b y y o u r i n s t r u c t o r -------------------------------------------------------------------------------------------------------------------------* / t i n c l u d e < io s tr e a m > u s in g nam espace s t d ; i n t m a i n () { s h o rt in t s i 1 , s i2 ; / / i n i t i a l d a ta d e c la ra tio n } Enter and save this code using the procedures required at your site. Check here when finished____ You have declared two s h o r t i n t variables s i 1 and s i 2. These two variables have memory locations associated with them where s h o r t i n t values can be stored. Our first objective is to determine the addresses of these memory locations. We can do this by using the ad d res s-o f o p e ra to r (&). The expression |&v a r i a b l e ] produces the address of the memory location associated with v a r i a b l e . Thus, the values of the expressions & s i1 and & si2 can be used to find the addresses of the variables s i l and s i 2. Add one or more output statements to the program a d d r e s s e s . c p p to find where your system has put the variables. Note: Addresses are typically displayed in hexadecimal (base-16) form—e.g., 0 x l 2 3 a e f 0 4 (remember that hexadecimal representations of integers in C++ begin with Ox). For some data types—e.g., c h a r —addresses may not display correctly on some systems; in this case, it may be necessary to typecast them to typeless values using a cast to v o id * : cout « ... « (void*)&.v a r i a b l e « ... Run your updated program and record the resulting addresses below: sil: ____________________________ s i 2 : _______________________________ 3 You declared the two variables s i l and s i 2 consecutively, and one would expect that they will be allocated adjacent memory locations. This makes it possible to determine how many bytes are allocated to them to store a s h o r t i n t . Since the address for a variable is the beginning address of a block of memory units for that variable, the difference between two adjacent variables’ addresses will be the size of the data type. Calculate the difference between the addresses of s i l and s i 2 and write it here:______________ The size of C++ data types is implementation dependent. Different implementations may use different numbers of bytes. It is fairly common, for example, to use 4 bytes for an i n t and 2 bytes for a s h o r t i n t , but this is not guaranteed. 12 Lab 2.1: Exploring Addressing Memory Maps Memory maps are diagrams that show the association (or mapping) between a program’s variable names and the memory locations that they occupy, usually given in hexadecimal notation. For example, suppose that characters are stored in 1 byte of memory and i n t values are stored in 4 bytes of memory. Then when the compiler processes the declaration: int intVal; char ch; it allocates memory for the variable intVal and then for ch. Compilers typically allocate memory from high addresses to lower addresses so that as more and more variables are allocated, the memory addresses get smaller. So if c h takes 1 byte and i n t V a l takes 4 bytes, the memory map might look as we see on the right. ML [44 ] In order to observe how the compiler allocates memory, you will modify the a d d r e s s . c p p program by adding a set of variables (two each) of i n t , l o n g i n t , f l o a t , d o u b le , l o n g d o u b l e , b o o l, and c h a r variables. A ddress O x...08 O x...09 0 x ...0 A Ox...OB 0 x ...0 C Ox...OD 0 x ...0 E Ox...OF Ox... 10 Location ch in tV a l ■ After s i l and s i 2 add declarations of the following, in the order shown: i n t variables i l and i2 l o n g i n t variables l i l and l i 2 f l o a t variables f l and f2 d o u b le variables d l and d2 lo n g d o u b le variables l d l and ld 2 b o o l variables b l andb2 c h a r variables c l and c2 Check here when finished_____ S» jT] 5 Add output statements to display the addresses of s i l , s i 2 , i l , i2 , l i l , l i 2 , f 1, f 2, d l , d2, l d l , ld 2 , b l , b2, c l , c2, and record the addresses. s il: si2 : il : i2 : l i l : li2 : fl: f2: dl: d2 : l d1 : ld2 : bl: b2: c l: c2 : 6 In the space at the top of the next page (or attach a separate sheet if you prefer), sketch a memory Mi [6] map of the memory allocated to these variables. Use as many strips of memory as necessary. You need not use a separate box for each byte—just draw blocks of memory whose sizes indicate roughly the amount of memory allocated for that variable and label the beginning and ending addresses of the blocks. Also, if the memory addresses are quite long—e.g., 0xl230f4ad0—just use the last three or four (hexadecimal) digits—e.g., 4ad0. 13 Lab 2.1: Exploring Addressing [T] From these displayed addresses we can determine how many bytes are used to store a value of a particular type. For example, if variables t l and t 2 have declarations of the form T t l , t 2 ; and the address of t l is a 1 and the address of t 2 is a 2, then values of type T are stored in lax - a 2l bytes (where I I denotes absolute value). Do this calculation for the data in the previous experiment. (Note that addresses are generally given in hexadecimal, so you will either have to convert to decimal or do your calculations in hexadecimal. You could have the computer do it for you by casting the addresses to i n t s and including the appropriate computations.) Determine how many bytes are used to store values of each of the following types: short in t i n t _ _ _ _____ l o n g i n t _________ d o u b l e ___________ l o n g d o u b l e _________ b o o l ___________ flo a t _ _ _ _ _ _ c h a r ___________ 8 The sizeof o p e ra to r has two common forms: |s i z e o f ( T ype) or s i z e o f v a r i a b l e l It produces the number of bytes allocated for an object of type Type, while the second form gives the number of bytes allocated for an object named v a r i a b l e . Using one of these forms, add statements to your code from Step 6 to display about how many bytes are used to store the various data types. Enter the results below: sh o rt in t d o u b le in t flo a t Io n a i n t lo n g d o u b le char bool Are the results you obtained the same as you obtained earlier? YIN _____ _ (You may find some “gaps” in your memory map caused by memory-allocation rules for a particular system—e.g., it might have a rule that all beginning addresses of allocated memory blocks must be divisible by 8.) 9 The d ere feren cin g o p e ra to r * is used to access the value stored in a memory location. Show this by putting the following statements in your program: s i l = 12345; c o u t << " s i l = " << s i l « e n d l << " A d d r e s s o f s i l = " << & s i l << e n d l << " C o n t e n t s o f " << & s i l << "= " << * ( & s i l ) What output is obtained? Y o u have finished. H a n d in : & s i l ___________________ * (& sil) << e n d l ; _______________________ 1) the lab with answers filled in, and 2) a printout of a listing of your final program. 14 Project 2.1 Binary Representation Project 2.1 Binary Representation O b je c t iv e : The b i t w i s e b in a r y o p e r a t o r s allow “bit twiddling” and are useful for many detailed data tasks such as manipulating data at the bit level. In this project you will use the bitwise operators to examine how integer values are stored internally in memory locations to determine whether your system uses the t w o ’s c o m p le m e n t r e p r e s e n ta tio n of integers described in Section 2.2 of the text JANUARY, 11111010101 1 10 11 100 101 111 110 1000 1001 1010 1011 1100 1101 1110 1111 10000 10001 10010 10011 10100 10101 10110 10111 11000 11001 11010 11011 11100 11101 11110 11111 A D T s, D a ta S tru c tu re s, a n d P r o b le m S o lv in g in C++, 2E. In this project you will use the bitwise binary operators: AND &, OR | , XOR (Exclusive OR) ^, NOT~ , SHIFT-LEFT <<, SHIFT-RIGHT >>, and shortcut versions used with the assignment operator (&=, | =, ^=, ! =, <<=, >>=) as described in the following table: D escriptio n: Produces result Expression x & ANDing the bits of x with those of y Example: 21 & 7 = 10101 & 00111 = 00101 = 5 y X I y x A y ORing the bits of x with those of y Example: 21 | 7 = 10101 | 00111 = 10111 = 23 XORing the bits of x with those of y Example: 21 ^ 7 = 10101 ^ 00111 = 10010 = 18 Inverting (complementing) the bits of x (0—>1 and 1—>0) Example (assumine 16-bit representation): For s h o r t u n s i g n e d value: ~7 = 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 = 65528 For s h o r t i n t value: ~7 = 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 = -8 Shifting the bits of x to the le ft y positions Example: 21 « 3 = 10101 « 3 = 10101000 = 168 ~x x << y x >> y Shifting the bits of x to the rig h t y positions Example: 21 » 3 = 10101 » 3 = 00010 = 2 >1 II 81 X Shortcut versions (with assignment) are also provided: &=, | =, ^ =, « = , » = Examnle: x &= y ; is equivalent to x = x & y ; Step 1: Write and test a function p r i n t B i n a r y () that receives an i n t value and displays its binary representation. For example, if integers are stored in 32 bits, it should display for the number 17 0000 0000 0000 0000 0000 0000 0001 0001 You will use the bitwise operations in your function. To begin your design, consider the following pseudocode outline of an algorithm: 1. Declare an unsigned int variable m a s k initialized to 2 n- 1 where n is the number of bits used in your system to store u n s i g n e d i n t values. For example, for n = 32, we can initialize m a s k with 0x80000000, which is equivalent to 10000000000000000000000000000000 in binary, and thus represents 231. 2. For each integer c o u n t in the range 1 through 32 do the following: a. If the bitwise AND of the given integer and m a sk is 1 Display ‘1’ Else Display ‘0 ’ b. Shift the bits in m a sk one position to the right. 15 Project 2.1 Binary Representation Work through an example by hand to ensure that you understand what the algorithm does. (It may be helpful to do an example with only 8 bits to get the sense of how the algorithm works.) • Write a function printBinary () that implements this algorithm—or if your instructor permits, you may use bitsets instead (see Extra Credit 2 below)—and incorporate it into a program to test it. Your program should repeatedly call for the user to enter an integer and then display it in binary. Execute your program with the five inputs shown in item 2 of the grade sheet on the next page and record the outputs produced. {Note: Do not be alarmed if the outputs fo r -2147483648 and -1 look a bit “strange.”) • Does your system use two’s-complement representation (see Section 2.2 of the text) for integers? Record your answer on the grade sheet. • Extra Credit 1: If f l o a t values require the same number of bits as i n t s on your system, change the parameter type of p r i n t B i n a r y () from i n t to f l o a t and do the same for the corresponding argument in the calling function to determine whether your system uses IEEE representation (see Section 2.2 of the text) for f l o a t values. Record your conclusion on the grade sheet and hand in two or three sample executions that demonstrate your conclusion. • Extra Credit 2: Study the description of bitsets in Section 9.8 of the text and use bitsets instead of the mask-shift algorithm to obtain a simpler version of printBinary ( ) . Once you are confident that your printBinary!) function is correct, incorporate it into a bitwise-operator calculator program that reads lines of input, each of which contains a bitwise operator and the appropriate number of operands—for example, Step 2: & 13 27 ~ 1 » 960 8 and which then outputs the integer operand(s), the bitwise operator, and the result obtained by applying the operator to the operands, with all numbers displayed both in decimal form and in 32-bit binary representation. • Use a function to display the output in a format similar to that on the next page; it should receive the operator, the operand(s), and the result. The function should call printBinary () to display the binary representations. Suggested output format: Applying the bitwise AND operator & to the following 13 27 = = 00000000000000000000000000001101 00000000000000000000000000011011 9 = 00000000000000000000000000001001 produces Applying the bitwise NOT operator - to the following 1 = 00000000000000000000000000000001 = 11111111111111111111111111111110 produces -2 Applying the bitwise RIGHT-SHIFT operator » 960 = to the following 00000000000000000000001111000000 with a shift of 8 bit positions produces 3 = 00000000000000000000000000000011 • Execute your program with the operands and operations listed on the grade sheet on the last page and add at least five more lines with vour own data. • Extra Credit 3: Design your program to read from a data file containing the lines of data and produce output to another file. Hand in copies of your input and output files along with the program. H a n d in the items listed on the grade sheet. 16 Project 2.1 Binary Representation Course Info:________________ Name:___________ Project 2.1 Grade Sheet Hand in: This grade sheet attached to the following: 1. A listing of the program you used to test your p r i n t B i n a r y () function. 2. Record here (or attach) the output produced for the test inputs: 17__________________ ______________________________________________________ 32768_____________________________________________________________ ___________ 2147483647________ _______________________________________________________________ -2147483648_________________________________________________________________________ - 1 _________________________________________________________________________ Does your system use two's-complement representation fo r i n t s (Y/N )?___________ 3. 4. 5. A listing of your bitwise-calculator program Evidence that this program compiles and links correctly The output produced by executing your program with the following lines of data and another 5 lines of your own: I f you do the extra credit, attach copies o f your input and output files. & 13 27 ~ » 960 8 & 65535 1 | 15 241 ^ 15 241 ~ « « » » Write your five lines o f input data here: 1 _____________________________________________ ___________________________________________ 1 15 4 9 31 240 4 240 6 Category _____________________________________________ _____ Points Possible Correctness (including following instructions).... ............... 80 Design and structure of program.......................... ............... 15 Use of functions.................................................... ............... 15 Programming style................................................ .............. (30) Program appearance...................................... ............... 10 Identifier names............................................. ............... 10 Documentation.............................................. ............... 10 Extra Credit 1 (+5 points) Does your system use IEEE for f l o a t s ? Extra Credit 2 (+15 points) Extra Credit 3 (+15 points) Total 140 17 _______________________ Points Received Lab 2.2: Create Your Own Types— Enumerations Course Info:_____________ Name:__________________________ Lab 2.2 Create Your Own Types—Enumerations Background: Predefined C++ data types are not adequate to model all the complexity of real-world phenomena. The result is that we sometimes need to build our own data types to model complex objects, either because they are nonnumeric and nonalphabetical or because they are heterogeneous (i.e., have multiple attributes). The objective of this lab exercise is to show how it is possible to extend the programming language C++ by adding new data types—albeit simple ones. Key Shale Limestone Sandstone Objective: In this lab exercise you will be learning how to define and use enumerated data types. These are described in Section 2.3: “Programmer-Defined Data Types” of the text ADTs, Data Structures, and Problem Solving with C++, 2E, and you should study it in conjunction with this lab exercise. You will be using the program g e o l o g y .c p p . You should get a copy of this code using the procedure specified by your instructor. (It can be downloaded from the website given in the preface.) You will be extending the code in the course of doing this lab. Problem: We will model and process rocks of different types: basalt, dolomite, granite, gypsum, limestone, marble, obsidian, quartzite, sandstone, and shale. A Simple but Awkward Approach We can give each rock type a unique integer code—e.g., 0 for basalt, 1 for dolomite, 2 for granite, etc.—and use the built-in type i n t to model rock values. We might use code segments like the following: i n t ro c k l, / / Make ro c k l = / / Make ro c k 2 = ro c k 2 ; / / v a r ia b le s to s to r e ro c k s ro c k l re p re s e n t b a s a lt 0; ro c k 2 r e p r e s e n t o b s i d i a n 6; To enter the two rocks basalt and obsidian for these two variables via a statement c i n >> r o c k l » ro c k 2 ; the user would have to know that 0 should be entered for r o c k l and 6 for ro c k 2 . This approach has a number of serious difficulties: • The programmer and user must remember the numeric codes of the rocks. • The situation gets worse as we add more and more rock types. • There are many things the programmer has to remember to maintain the code, and the problem gets worse when other programmers have to maintain the code later. 19 Lab 2.2: Create Your Own Types— Enumerations A So m ew h at B e tte r A p p ro a c h Look at the program geology. c p p to see what it does. Note the long sequence of c o n s t declarations that associate meaningful names with the numeric codes of the rocks. 1 Study the declarations and loops in the m a in () function. Now “reverse engineer” the program by writing an algorithm for m a i n ( ) in pseudocode. (See p. 16 of the text for an example of an algorithm written in pseudocode.) 2 Now compile and execute the geology.cpp program. WARNING: MAY NOT WORK AS YOU EXPECT What happens if you enter the name of a rock such as BASALT? Now try entering the numeric codes of basalt, dolomite, and granite. Describe the output produced. While the code itself is more readable, it is not clear that anything has been gained from the point of view of the user running the program. He or she has to input numeric codes, and the output consists of numeric codes. We’d really like the compiler to help us in matters such as these. An Alternate Approach Using Enumerations A B rief Summary o f Enumerations (Text: pp. 60-62). The earliest programming languages had no mechanisms by which a programmer could create new data types, so that one could use only the built-in data types. We’ve just seen how numerical codes can be used to represent what are called “scalar” or “enumerated” data types. C++ provides a built-in enumeration mechanism for defining such data types, as described on pp. 60-62 of the text.) One of the examples in the text is a declaration of an enumerated data type Color whose values are the colors RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, and VIOLET followed by a special value COLOR_OVERFLOW. enum Color (RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET, COLOR_OVERFLOW); 20 Lab 2.2: Create Your Own Types— Enumerations C o l o r acts as a new data type that was defined by the programmer. When the compiler encounters this new type, it performs a 1:1 mapping from the enumerators (i.e., the words used for the colors) to the integers, associating 0 with the first enumerator RED, 1 with ORANGE, and so forth, one by one, until it associates 7 with COLOR_OVERFLOW. Using a slightly different syntax, we can specify the integers to be assigned to the enumerators; for example, enum N u m b e rB a se {BINARY = 2 , OCTAL = 8 , HEXADECIMAL = 1 6 } ; DECIMAL = 1 0 , HEX = 1 6 , Associating the base’s value with its name allows us to use the names in expressions such as 2 *HEX + 1. We can also cause the compiler to start numbering with a value other than 0 by specifying the value for the first enumerator. The compiler, by default, will simply add one for each subsequent enumerator. enum C o l o r {RED = 1 , ORANGE, YELLOW, GREEN, BLUE, COLOR_OVERFLOW}; INDIGO, VIOLET, This will produce RED = 1, ORANGE = 2, YELLOW = 3, GREEN = 4, and so forth until COLOR_OVERFLOW - 8. If we wanted the last four enumerators to be associated with 10, 11, 12, and 13, we could use enum C o l o r {RED = 1 , ORANGE, YELLOW, GREEN, BLUE = 1 0 , COLOR_OVERFLOW}; INDIGO, VIOLET, 3 Now we want to modify Part 1 of g e o l o g y . c p p as follows. Eliminate the c o n s t definitions and replace them with the declaration of an enumerated data type called R o ck . You might initially simply comment them out and use them as a guide to write the enumerated data type declaration. When you’ve finished that, change the type of s a m p l e from i n t to R o c k . Then recompile and execute the program, noting any errors that occur. What happened? WARNING: MAY NOT WORK AS YOU EXPECT We’ve encountered a problem with the input operator » . It is not defined for type R o ck . This will be fixed in Project 2.2, but for right now, so that we can proceed, we’ll do something “quick and dirty”—not recommended for general use. 4 Replace the statement c i n >> s a m p l e ; with in t te m p ; c i n >> te m p ; s a m p le = R o c k ( te m p ) ; / / i n p u t 'a n in te g e r / / c a s t th e i n t e g e r t o R o ck t y p e Now recompile and execute the program as you did before, entering the numeric codes for basalt, dolomite, and granite. Describe any differences (if any) between this program’s output and that in our earlier attempt in step 2. A lot of work with not much gain, right? It should be clear by now that enumerated variables are only a sort of “shell” over the integer values they are assigned to represent. The compiler just makes substitutions for them. 5 Now we are going to modify g e o l o g y . c p p further. Specifically, change the type r o c k V a l in the second f o r loop from i n t to R o c k . Then attempt to recompile and execute the program. What happened? 21 Lab 2.2: Create Your Own Types— Enumerations Implementing a Data Type In trying to use the enumerated data type to correct the rather awkward approach of doing it all manually the way it was done in the original geology, cpp, we’ve encountered several problems. This time we found that using the Rock type for rockVal revealed the fact that Rock has no ++ increment (prefix or postfix) operator associated with it. A data type has two parts: 1. A collection of values, and 2. Operations that are defined for using and operating on those values. Our enum declaration does only the first part for our new data type Rock—it specifies the collection of values (enumerators). But that is not enough. We also have to define functions that carry out the operations on Rock values. Also, after putting in all that work, we would also like to have our new data type reusable, so we want to package it into a library (and later a class). For now our library will consist of two files: 1. A header or interface file, and 2. An implementation file which contains the definitions of the functions whose prototypes are in the header file. Header file (Interface file) The header file performs a dual interface role. • It informs the compiler what the implementation will contain. In our example, it will contain: o The enumeration type declaration o Prototypes of the functions that carry out the operations • It acts as documentation for the user of the interface/implementation combination. As a result it should contain thorough documentation of the following forms: o Opening documentation, and o Component documentation Opening documentation should typically include the following elements: 1) The name of the programmer(s) 2) The organization (typically in a college environment it might be the name of the college, the course and section numbers, and any items specified by your instructor) 3) A general description of the data type being defined, including its purpose, list of operations, and any special instructions For example, the opening documentation in R o c k . h might be something like the following: /*-- R o c k . h ------------------------------------------------------ ---A header file for an enumerated data type Rock that models rocks of various types (basalt, dolomite, granite, gypsum, limestone, marble, obsidian, quartzite, sandstone, and shale) Operations are: next(): A successor function other operations ... W ritte n by: W ritte n fo r: J o h n Doe CS 112A U n iv e rsa l U n iv e rs ity S e p t . 9, 2005 ------------------------------------------------------------------------------------------------------------------- * / 22 Lab 2.2: Create Your Own Types— Enumerations Component documentation should include: 1) Any comments about the data type being defined if it is not self-explanatory. 2) Documentation to accompany each of the function prototypes that explains to the user: • Purpose of the function—what it does • Preconditions that must hold before the function can be used • Postconditions that result after the function has been executed 6 In your current working directory, create a header file R o c k . h containing: • The required opening documentation and • The R ock enumeration declaration. You may copy and paste this from your earlier g e o l o g y . c p p file. Then remove the enumeration declaration from your g e o l o g y . c p p file and add the line # i n c l u d e " R o c k .h " to g e o l o g y . c p p below the u s i n g n a m e sp a c e s t d ; line Now recompile g e o l o g y . c p p and make sure that you don’t have any new compilation errors, except for the ++ error we encountered in step 5. Check here when finished______ Defining a Successor Operation Now it is time to fix the problem that we encountered in step 5 when we found that the R o c k data type didn’t have an associated increment or successor operation. You may not have immediately realized that was the problem. The code for the f o r loop in Part 2 of the g e o l o g y . c p p code reads: f o r (R o c k r o c k V a l = BASALT; r o c k V a l < ROCK_OVERFLOW; rockVal++) c o u t << r o c k V a l << “ " ; The increment operator ++ that is defined for integers doesn’t work because it is not defined for R o ck objects. You will fix that by constructing a n e x t () function that returns a successor of a R o c k and that will be used instead of the ++ operation. Later (Project 4.1) we will learn how to overload C++’s + + operator for type R o ck so that the f o r loop will operate as originally written. 7 Modify the f o r statement in Part 2 of g e o l o g y . c p p to use the n e x t () function instead of the + + operator to move r o c k V a l from one R o ck value to the next: fo r (R o c k r o c k V a l = BASALT; r o c k V a l < ROCK_OVERFLOW; rockVal = next(rockVal)) c o u t << r o c k V a l << " "; Now we need to develop the n e x t ( ) function. As with all function development, we begin by formulating a specification. A good way to begin is to identify what things are to be passed to the function (if any) and what the function is to return or what other actions it is to carry out. For n e x t ( ) , we have: Receives: A R o c k value r o c k V a l Returns: The successor of r o c k V a l (The successor of the last enumerator SHALE is ROCK_OVERFLOW.) 23 Lab 2.2: Create Your Own Types— Enumerations 8 Now we must add a prototype for the next () function to the Rock.h header file: Rock next(Rock rockVal); But that is not enough. Header files are also called in te r fa c e f i l e s because they contain the information someone needs to know in order to use the new data type, which includes the functions that carry out the operations on that data type. This means that we must add appropriate documentation for these functions. There are different ways to do this and often organizations will have specific requirements which their programmers are expected to follow. The minimum documentation should describe: 1) The p u r p o s e of the function—what it does 2) The function’s p r e c o n d itio n s — what must be true when the function is called (specific inputs such as a valid ro c k V a l) 3) The function’s p o s tc o n d itio n s — what must be true when the function finishes execution (for example, next () returns the successor to the Rock value sent to it) 4) Any other information a programmer would need to use the code. Annotation/Commenting and Programming Style— The single most important property that code should have is being e a s y to r e a d a n d u n d e rsta n d . Two obvious things to contribute to this are to: • Use meaningful parameter names, function names, variable names, and user-defined type names. • Use a consistent style for code to enhance readability—e.g, indent blocks of loops and selection statements, properly align statements, use horizontal white space between items in expressions and vertical white space to separate chunks of code. Another important way to is to provide good documentation for each function. In the text, this is added to each function prototype in the header file (although some programmers prefer to separate it out and put it in a separate documentation file or after the opening documentation of the header file). For example, Rock next(Rock rockVal); / * ----------------------------------------------------------------------------------------------------------------- Find the successor of a Rock value. Precondition: rockVal is a valid Rock value. Postcondition: The successor of rockVal has been returned. (The successor of SHALE is ROCK_OVERFLOW and ROCK_OVERFLOW is its own successor.) ----------------------------------------------------------------- */ Some programmers prefer to put such documentation on the right side of the line of code and indent subsequent lines: Rock next(Rock rockVal); // // // // Accepts a valid Rock value and returns the successor rock value. For SHALE and for ROCK_OVERFLOW, ROCK_OVERFLOW is returned. Now design and add a prototype and your particular documentation for the next () function to the R o c k . h header file. Check here when finished 24 Lab 2.2: Create Your Own Types— Enumerations Implementation file A header file tells what some functions are to do. An implementation file tells how those functions do their work. It contains: 1) The definitions of the operations (functions) on the new data type. The declarations of functions (function prototypes) are in the header file, while the definitions are in the implementation file. 2) These are preceded by a # i n c l u d e directive to insert the library’s header file. This allows the compiler to check that the declarations of prototypes agree with the headings of the function definitions. 9 Complete the skeleton definition of n e x t () provided below and put it in Rock.cpp: //-- Definition of next() for finding the successor of a Rock value Rock next(Rock rockVal) { switch(rockVal) { case BASALT: return DOLOMITE; default: //output an error message and return ROCK_OVERFLOW } } If necessary, review how the C++ s w i t c h () statement operates (see Appendix C.10 of the text). Why don’t the statements associated with the s w i t c h () statement’s cases in the n e x t () function require a b r e a k ; statement concluding the c a s e implementations? Now incorporate documentation into the implementation file. • The opening documentation should contain the information about the programmer that is in the header file (“Written by,” Written for”). In fact, if you like, you can just copy and paste the opening documentation from the header file and replace the word “header” in the opening line with “implementation.” • Each function definition in the implementation file should be annotated with a short statement of what is being defined. This can usually be done with no more than one or two lines of commentary. D on’t forget to #include "Rock.h " in your Rock.cpp implementation file. This will cause the preprocessor to insert the contents of the header file at the beginning of the implementation file so that the compiler can check function prototypes against their definitions. 10 Now you are ready to use the compiler to translate the files to machine language, the linker to combine these machine-code files together to form a binary executable, which can then be executed. Some IDEs (Integrated Development Engironments) will generate the steps for this automatically, for example by selecting the Build menu item in Visual C++. Regardless of whether it is done automatically for you, the process is composed of two phases: 1) Compiling, in which C++ code is translated into linkable machine-language modules; and 2) Linking, in which calls to functions outside the main file are linked to their definitions. 25 Lab 2.2: Create Your Own Types— Enumerations Thus, to create the binary executable that is finally loaded into the operating system to run, all the implementation files must be compiled as well as the main program. The resulting object-code files are then linked together to create the binary executable. Here is a typical sequence that can be executed manually in the Unix systems that use the gnu C++ compiler: Step 1: Separately compile the program file n a m e . c p p to produce an object file named n a m e . o in some systems. This may require using a special compiler switch, as in the gnu C++ command g++ - c g e o l o g y . c p p Step 2: Separately compile each library implementation file in the same manner: g++ - c R o c k . c p p This causes the compiler to generate an object file R o c k . o. Step 3: Link the object files together into a binary executable program—for example, in gnu C++ with the command g++ g e o l o g y . o R o c k . o - o g e o l o g y This command causes the two object files to be combined into the executable file named g e o l o g y . This can be executed by using the command g eo lo g y or . /g e o lo g y (if the current directory has not been added to the PATH environment variable). If the linking command has been given in the form g++ g e o l o g y . o R o c k . o the default name a . o u t will used by Unix for the binary executable. Note: The compiling and linking steps can all be done with a single command: g++ g e o l o g y . c p p R o c k . c p p - o g e o l o g y Now, compile, link, and execute your g e o l o g y . c p p program. Briefly describe its behavior below. As a program begins to use more and more libraries, it becomes increasingly difficult to keep track of which files are out of date and need to be recompiled and which do not. Single commands such as we have been using illustrate how the process can be done when the number of files is small. This becomes cumbersome, however, as the number of files increases. Most IDEs provide projects that bundle the files together, and a single command can be used to compile them all and link them together. In Unix, one can use make files to automate the process. The make utility executes a file named Makefile in the current directory that contains commands that control the compilation and linking process. 26 Lab 2.2: Create Your Own Types—Enumerations If you are using a Unix environment: Download the M a k e f i le for this lab if you haven’t already done so; be sure it is in the same directory as g e o l o g y . cp p , R o c k . h, and R o c k . cp p . Then give the command m ake. You should see only relinking take place because the files have already been compiled. Check here when finished___ Now make a small change in g e o l o g y . cp p , such as adding a blank line or a space at the end of some line, and give the m ake command again. This time you should see g e o l o g y . c p p being recompiled (but not R o c k . c p p because it is up to date) followed by the linking. Check here when finished__ _ Now make a similar small change in R o c k . h and give the m ake command again. This time you should see recompilation of both g e o l o g y . c p p and R o c k . c p p and then the linking. This is because they both have been modified since they include R o c k . h which was changed. Check here when finished___ A d d in g a N e w O p e ra tio n to the R o c k D a ta T yp e 11 You are now going to enhance the Rock data type by adding a new operation—determining whether a rock is igneous, metamorphic, or sedimentary. Specifically, you are to add a new function kind () to type Rock that returns one of the strings “igneous,” “metamorphic,” or “sedimentary,” indicating the kind of rock, given that: • Basalt, granite, and obsidian are igneous. • Marble, and quartzite are metamorphic. • Dolomite, limestone, gypsum, sandstone, and shale are sedimentary. You can use the next ( ) function as a model. Before writing the kind() function you should consider what information it needs and what information is should return. What information must you send to your kind () function? What information will your k i n d () function return? Write the kind () function and add appropriate documentation to the Rock. h and Rock. cpp files. ★ Note that since this function is returning a string value, you will have to include thelibrary in Rock.h. Check here when finished____ 12 Now add a for loop in geology.cpp to test drive the kind () function by displaying the kinds of all the rocks. Compile, link, and execute the modified program to ensure that kind () works. Check here when finished You have finished. Hand in the items listed on the grade sheet on the next page. 27 Lab 2.2: Create Your Own Types— Enumerations L a b Exercise 2.2 G ra d e Sheet N am e: H a n d in: 1. This lab handout with the answers filled in 2. Attach printouts showing: a. Listings of R o c k . h and R o c k . c p p b. A listing of g e o l o g y , cp p c. A demonstration that everything compiles and links correctly d. A sample run of g e o l o g y . c p p Category Points Possible Conmletion of Lab Handout........................................... (35) Driver Program......................................................... ............(25) Correctness (including following instructions) ............ 20 Style/Readabil ity/Documentation.................... ............. 5 Header File................................................................ ........... (35) Opening Documentation.................................. ............ 10 Specifications.................................................... ............ 10 Style/Readability............................................... ............. 5 Enumeration Declaration and Function Protot)rpes..... 10 Implementation File.................................................. ........... (30) Correctness........................................................ ............ 25 Style/Readability............................................... ............. 5 Adequate Testing of Library .................................. ........... (15) Total.......................................................................... .............140 28 Points Received Project 2.2 Rock Operations Project 2.2 Rock Operations Objective: You are going to extend the R o c k data type developed in Lab 2.2 by adding input and output operations. You will be overloading the output operator << and the input operator >>. Once you have added these operations to the R o c k type and tested them, you will use your new R o c k type in a program that reads a file of rock names, uses an array indexed by R o c k to count occurrences of each rock, and then displays the counts in a histogram (bar graph). Background: A Short Tutorial on Overloading Operators C++ allows a function name to be overloaded provided that no two versions of the function with this name have the same signatures. (The signature of a function is its list of parameter types.) Most C++ operators such as +, - , <, <<, and >> can also be overloaded, which makes it possible to define them as operations on new data types that we create. (The few that cannot are listed in Table C.2 in Appendix C of the text ADTs, Data Structures, and Problem Solving with C++, 2E.) Overloading an operator A in C++ is accomplished by overloading a corresponding function whose name is o p e r a to r A . For example, the function corresponding to the addition operator + is o p e r a t o r + , which means that a + b can also be written o p e r a t o r + ( a , b) If a and b are i n t s , the signature of this version of o p e r a t o r + () is ( i n t , i n t ) and its return type is i n t ; it returns the integer sum of a and b. If a and b are d o u b le s , the signature of this version of o p e r a t o r + () is ( d o u b le , d o u b le ) and its return type is d o u b le ; it returns the real sum of a and b. If a and b are s t r i n g s , the signature of this version of o p e r a t o r + () is ( s t r i n g , s t r i n g ) and its return type is s t r i n g ; it returns the string obtained by concatenating b onto a. Similarly, the function corresponding to the multiplication operator * is o p e r a t o r * () ; the function corresponding to the division operator / is o p e r a t o r / (); the function corresponding to the less-than operator < is o p e r a t o r < () ; the function corresponding to the logical and operator && is o p e ra to r& & () ; the function corresponding to the output operator << is o p e r a t o r < < (); the function corresponding to the division operator >> is o p e r a t o r » () ; and so on. Thus, to overload an operator A, we need to define a function of the form: R e tu r n T y p e o p e r a t o r _ (p a r a m e t e r _ l i s t ) { } 29 Project 2.2 Rock Operations Adding an Output Operator to Type Rock After building the R o c k type in Lab 2.2, we still have I/O problems. Displaying a value of type R o c k gives one of the integers 0, 1, 2,..., 9, which are assigned by the compiler to the R o c k enumerators, instead of the name of a rock. And to input a value of type R o c k , we cannot enter the R o c k ’s name but rather must enter the integer associated with it. We will address the output problem first. We could just write a d i s p l a y ! ) function similar to the n e x t () and k i n d () functions in Lab 2.2. But if we want the output operator to work with output streams like the standard output stream c o u t for new data types like R o c k in the same way that it does for built-in data types, then we must overload the output operator <<. As we saw earlier, we must write a function of the form ReturnType o p e r a t o r « (parameter_list) { } So we must figure out what to use for ReturnType and the parameter_list. We first look at the problem of determining the parameter list. From what we saw earlier, an expression of the form c o u t << r o c k V a l corresponds to the function call o p e r a t o r « (c o u t , r o c k V a l ) From this we see that the function o p e r a t o r < < () needs two parameters: ReturnType o p e r a t o r « ( _________ o u t , __________ r o c k V a l ) { } We need to determine what types to put in the blanks for the parameters. The type of r o c k V a l clearly is R ock, ReturnType o p e r a t o r « ( _________ o u t , Rock r o c k V a l ) { } The type of the first parameter o u t needs to be o s t r e a m , but in fact it must be a reference parameter so that it refers to the same o s t r e a m object as—i.e., is an alias for—the corresponding argument ( c o u t ) . This is because when we output something to o u t , we want it to actually go to c o u t —if o u t were a value parameter instead, it would be a copy of c o u t (which may, in fact, cause an error). So. to output a R o c k value, we need the signature ( o s t r e a m &, R o c k ) ; thus the function becomes ReturnType o p e r a t o r « (o s tr e a m & o u t , Rock ro c k V a l) { } We still need to determine what the return type should be. Since the left-hand side of an output expression is an output stream variable, the function must return a variable of that type if we want to chain expressions together such as c o u t << r o c k V a l « " " « a n o t h e r R o c k << e n d l ; Moreover it needs to be a reference variable. Why? Because an output stream is an object and we don’t want a copy returned but rather the object itself. Thus we need to return a reference to the output stream. 30 Project 2.2 Rock Operations With this information we now know what the form of an overloaded output operator for our Rock type should be: ostream & operator<<(ostream & out, Rock rockVal) { // statements to output to output stream out a text representation / / o f the Rock datatype value rockVal passed as an argument return out; } Now, all that remains is to write the statements that output a text representation of rockVal. When a Rock value is used in an output statement like cout « rockVal « endl; //rockVal being of type Rock we want the output to be BASALT when the rockVal is BASALT and SHALE when the rockVal is SHALE, and so on. To accomplish this, we need a Rock-to-string converter that will display the text string that corresponds to a Rock value. g t e p j j A s w i t c h statement is ideal for this kind of task. Its cases perform the Rock-to-string conversions; for example, case BASALT: out « "BASALT"; (Remember to put break statements after each case so execution doesn’t “fall through” from one case to the next.) Add a prototype for this new function along with appropriate documentation to your R o c k .h header file and the definition to your R o c k .c p p file. Then modify the g e o l o g y , c p p program to test the newly overloaded operators. Recompile, link, and execute the program, entering the numeric codes of the rocks—you haven’t implemented an input operator » yet—and observe the output produced. Check to see whether the correct rock names were displayed. A d d in g an In p u t O p e ra to r to T y p e Rock Now that you’ve seen how to overload the output (insertion) operator <<, you should be able to do the input operator. You are to write it so it will accept any combination of upper- and lower-case letters spelling out its particular enumeration, e.g., Basalt, BASALT, BasAlt, and so on. To accomplish this you might need to use some of the string functions, such as the functions i s l o w e r () and t o u p p e r () from < c c ty p e > , to convert the individual characters in the input string to all upper case in a loop of the form for (int i = 0; i < str.length(); i++) if (islower(str[i])) str[i] = toupper(str[i]); Step 2: Write your new overloaded input (extraction) function so that it properly handles statements like cin >> sample; //where sample is of type Rock and will assign, for example, BASALT to sample if the user enters BASALT, basalt, Basalt,.... You have to be careful here, because the word BASALT can be either a string or a Rock value depending on the context. Give some thought to how you will accomplish this. 31 Project 2.2 Rock Operations By analogy with the output prototype , your input operator prototype will look like: is tre a m & o p e ra to r> > (is tre a m & in , R ock & r o c k V a l ) ; Notice that we are using a reference variable for the R o ck to be input. (What would happen if we didn’t and omitted the & ?) Add this prototype along with appropriate documentation to R o c k . h and add its definition to R o c k . cpp. (Remember: Strings cannot be used in s w i t c h statements as c a s e values.) When you have completed writing your overloaded input operator, adding it to R o c k .h and R ock, cp p , you will want to change the g e o l o g y . c p p program by removing the patch you used in Lab 2.2— i n t te m p ; c i n » te m p ; s a m p le = R o c k ( te m p ) ; —and restoring the original statement c i n > > sam pl e ;. Now recompile, link, and execute this latest version of the program. Take it for a test run. How did it do? If you have any problems, correct them and get it to run properly. A p p lic a tio n o f y o u r R o c k c l a s s Now that you have a tested Rock class with quite a few capabilities, it’s time to use it in an application: a bar-graph generator that will display the number of times each kind of rock appeares in a given data file. Program Requirements Write a program that uses your newly developed Rock type. You will run the program with the data file Rockfile.txt that contains a random collection of names of rocks. The program you write should do the following: 1. Declare an integer array count [ ] with all of its elements initialized to 0. 2. Read Rock values from the file RockFile.txt and for each rock, increment the appropriate element of count [ ] by 1; for example, if Basalt is read, then count [BASALT] should be incremented by 1. [Note that Rock values can be used as indices because each is associated with a nonnegative integer.] 3. Display the elements of count [ ] as a histogram (bar graph) something like the following: BASALT DOLOMITE GRANITE GYPSUM LIMESTONE MARBLE OBSIDIAN QUARTZITE SANDSTONE SHALE XXXXXXXXXXXXXXX (1 5 ) XXXXX (5 ) XXXXXXXXXXXX ( 12) XXX (3 ) XXXXXXXXXXXXXXXXXXXXXXXXXX ( 26) XXXXXXXXXXXXXX ( 14) XXXXXXX (7) (0) XX (2 ) X (1 ) where the length of each bar (the number of X’s) and the number in parentheses indicate the number of times that rock was found in the file. 32 Project 2.2 Rock Operations Name: Course Info: Project 2.2 Grade Sheet H a n d in printouts that contain the following items or additional items specified by your instructor: 1. 2. 3. 4. Listings of R o c k . h and R o c k . c p p A listing of the program you used to test the library A demonstration that your program and library compiled and linked correctly A sample run of the driver program that thoroughly exercises all of your R ock operations and demonstrates that they work correctly Attach this grade sheet to vour printouts. Points Possible Cateeorv Finishine the R o c k Date Twe: Header File............................................................... ............ (20) Opening Documentation................................................ 5 Function Prototypes and Specifications.......... ............ 10 Style/Readability............................................................. 5 Implementation File................................................. ............ (40) Correctness of Function Definitions............... ............ 30 Structure of Function Definitions................... ............. 5 Style/Readability............................................................. 5 Testing...................................................................... ............ (30) Driver Program............................................................... 15 Adequate Testing of Library........................... ............. 15 S u b to ta l ................................................................... ............. 90 Application: Correctness (including following instructions) ..... ............ 50 Structure .............................................................................. 5 Style and Documentation .....................................................5 S u b to ta l ................................................................................. 60 T o t a l ...................................................................................... 150 33 Points Received
Source Exif Data:
File Type : PDF File Type Extension : pdf MIME Type : application/pdf PDF Version : 1.5 Linearized : No Tagged PDF : Yes Page Count : 26 Producer : ABBYY FineReader 12 Create Date : 2015:01:26 18:49:24Z Modify Date : 2015:01:26 18:49:24ZEXIF Metadata provided by EXIF.tools