| Name: _____________________ | Class: CET 375 |
| SSN/ID: _____________________ | Section & Group: ____________ |
One view of object oriented programming (OOP) is programming
using data types to model real-world objects; that is, defining
data objects to model "real-world" objects and operating on those
objects to perform a computation. The problem is that the pre-defined
C++ types (int, double, char, etc.) are fine for modeling
numeric data and some text data but many real-world objects are not
numbers or characters. We want to create new data types to model such
non-alphanumeric objects. The objective of this lab exercise is to
show how it is possible to extend the programming language C++ by
adding these new data types to it.
→ In this lab exercise, you will be using the program source_lab04_geology.cpp. Please download this file to your home directory and rename it to geology.cpp.
Problem: To model and process rocks (basalt, dolomite, granite, limestone, marble, obsidian, quartzite, and sandstone).
Approach #1 (the "old-fashioned" approach)
Code each rock with an integer code and use int to model
rocks.
Difficulties with this approach:
int rock1, rock2;
// Set rock1 to basalt, rock2 to quartzite
rock1 = 0;
rock2 = 6;
Look at the program geology.cpp to see what it does. Note the sequence of const declarations. Compile the program and execute it.
Approach #3: Use an enumerated type.
The problem in the preceding approach is that we are representing real-world objects (rocks) with integer codes (0, 1, 2, ...) instead of using their "real-world" names (basalt, dolomite, granite, ...). C++ provides an enumeration mechanism that allows a programmer to define a new data type whose values are those real-world names. In this lab exercise, we will create a new data type Rock whose values are the names of the rocks, instead of integers.
A Brief Summary of Enumerations
In earlier programming languages, it was necessary to use numeric codes to represent real-world objects; for example, using 0, 1, 2, 3, ... to represent rocks instead of using their real-world names (asalt, dolomite, granite, limestone, ...). The problem with this is the difficulty of remembering these codes and reading instructions for processing objects using these codes. C++ provides the enumeration mechanism that allows a programmer to define a new data type whose values are those real-world names.
The following is an enumeration declaration for a new data type
Color whose values (called enumerators) are the colors
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, and
VIOLET followed by a COLOR_OVERFLOW value:
This declaration defines a new type, named Color, whose
values are the identifiers listed between the curly braces. (These
enumerators must be valid C++ identifiers.) When the compiler
encounters such a declaration, it performs an enumerator-to-integer
mapping referred to earlier, associating 0 with the first enumerator
in this list, 1 with the second, and so on. Thus, for the preceding
declaration, the compiler associates 0 with RED, 1 with
ORANGE, 2 with YELLOW, ..., 6 with VIOLET,
and 7 with COLOR_OVERFLOW.
enum Color {RED, ORANGE, YELLOW, GREEN,
BLUE, INDIGO, VIOLET, COLOR_OVERFLOW};
As another example, the declaration
declares a new type Gender whose values are the identifiers
FEMALE and MALE; the compiler will associate 0 with
FEMALE and 1 with MALE. Similarly, the declaration
enum Gender {FEMALE, MALE};
constructs a new type HandTool whose values are HAMMER,
PLIERS, SAW, and SCREW_DRIVER and associates the
integers 0, 1, 2, 3 with these identifiers, respectively.
enum HandTool {HAMMER, PLIERS, SAW, SCREW_DRIVER};
C++ also allows the programmer to specify explicitly the values given
to the enumerators. For example, the declaration
associates the identifiers BINARY, OCTAL, DECIMAL, HEX, AND
HEXADECIMAL with the values 2, 8, 10, 16, and 16, respectively.
Similarly, if we wished to have the values 1, 2, ..., 7 associated
with the seven colors given earlier (instead of 0 through 6), we could
use the declaration
enum NumberBase {BINARY = 2, OCTAL = 8, DECIMAL = 10,
HEX = 16, HEXADECIMAL = 16};
or, more compactly,
enum Color {RED = 1, ORANGE = 2, YELLOW = 3, GREEN = 4,
BLUE = 5, INDIGO = 6, VIOLET = 7, COLOR_OVERFLOW = 8};
because the integer associated with an enumerator is, by default, one
more than the integer associated with the preceding enumerator.
enum Color {RED = 1, ORANGE, YELLOW, GREEN,
BLUE, INDIGO, VIOLET, COLOR_OVERFLOW};
Replace the offending statement
with:
cin >> sample;
int temp; cin >> temp; sample = Rock(temp);
Header file
The header file is the interface between the data type and the user, so it must be documented thoroughly. It's opening documentation should include (at least) the following items (and other items from the Programming Style Sheet):
#include <iostream>
using namespace std;
directives at the beginning of this header file.
#include "rock.h"
at the beginning of geology.cpp and remove the
enumeration declaration of type Rock. Recompile
geology.cpp to check that there are no new compilation
errors — only the ++ error from 6.
We will first consider the problem in the for loop in Part 2
of geology.cpp:
(Note: in C, you need to declare Rock rockVal as enum Rock rockVal.)
for (Rock rockVal = BASALT; rockVal <= SANDSTONE; rockVal++)
cout << rockVal << " ";
The problem is that the increment operator ++ is defined for integers but not for Rocks. You will now construct a function Next() that returns the successor of a rock and use it in place of the ++ operation. (In Lab 4 we will see how the operator ++ can be overloaded for type Rock so that this for loop will execute.)
for (Rock rockVal = BASALT; rockVal <= SANDSTONE;
rockVal = Next(rockVal))
cout << rockVal << " ";
A specification for Next() is as follows:
Rock Next(Rock rockVal);
and appropriate documentation (as per the Programming Style Sheet).
→ Each function prototype should be accompanied by a block comment giving the following:
The implementation file contains:
Rock Next(Rock rockVal) {
switch (rockVal) {
case BASALT: return DOLOMITE;
case DOLOMITE: return GRANITE;
. . .
default:
// Output an error message and return the ROCK_OVERFLOW value
}
}
Compiling and Linking
The directive #include "rock.h" causes the compiler (actually, the preprocessor) to insert the declaration of Rock and the prototype for function Next() into geology.cpp. However, the definition of Next() is in rock.cpp, not in geology.cpp.
Translating a program like geology.cpp to produce a binary executable program involves two phases:
g++ -c geology.cpp
| geology.cpp | → | geology.o |
g++ -c rock.cpp
| rock.cpp | → | rock.o |
g++ geology.o rock.o -o geology.exe
| geology.o | → | geology.exe |
| rock.o | → |
If a program involves several different libraries, it can be very difficult to keep track of which files are out of date, which need to be recompiled, and so on. Single commands like the preceding that do this automatically are very usefull in this regard. With GNU C++, UNIX's make utility can be used to execute a file named Makefile that contains the commands for the compilations and linking.
Defining a Rock Kind Operation
Hand In:
| Category | Points Possible | Points Received |
|---|---|---|
| Completion of Lab Handout | (45) | __________ |
| Driver Program | (25) | |
| Correctness (including following instructions) | 20 | __________ |
| Style/Readability/Documentation | 5 | __________ |
| Header File | (35) | |
| Opening Documentation | 10 | __________ |
| Specifications | 10 | __________ |
| Style/Readability | 5 | __________ |
| Enumeration Declaration and Function Prototypes | 10 | __________ |
| Implementation File | (30) | |
| Correctness | 25 | __________ |
| Style/Readability | 5 | __________ |
| Adequate Testing of Library | (15) | __________ |
TOTAL |
150 |
__________ |