CS 2A Object-Oriented Programming in C++Assignment 7
SYLLABUS
ASSIGNMENTS
LESSONS
SOLUTIONS
Assignment 7.1 [85 points]
This program will ask the user to answer a series of arithmetic problems and report on how the user performs. You will write this program in phases as specified below. Make sure that each phase works correctly and uses
good style before progressing to the following phase.
The purpose of this assignment is to give you lots of practice with parameter passing without making you write a huge program. For this reason you must adhere to the following structure diagram. If you fail to do this,
you will receive a 0 on the assignment. Each box represents a function that you will have in your program. It includes every function that you will have in your program. You should have no more and no fewer, and you
should use the exact names that have been indicated here. The functions won’t make sense just yet. You’ll need continually refer back to this diagram as you work your way through the phases.
Structure Diagram Notes:
The doOneProblem function does exactly one problem, not one *type* of problem! This function will perform all of the tasks involved in finishing one complete problem, including printing the problem, checking the
answer, etc.
CheckAnswer is the function that writes either “correct” or “incorrect”
You will receive a 0 on this assignment if you use global variables, arrays or structs
You will lose points if the code you put in one of your functions does not correspond to the name of the function given in the structure diagram.
Do not use value returning functions in this assignment. The purpose of this assignment is to give you practice with parameter passing, including pass-by-reference, and you could miss much of this experience
if you use value returning functions instead.
Definitions: As a quick review of arithmetic, make sure you are clear that the “operands” in an expression are the things that appear on either side of the operator (numbers in this case). Don’t get that mixed up with
the operator. The operators are +, -, and *.
Phase 1: Your main() function for phase 1 must look exactly like this:
int main()
{
srand(static_cast(time(0)));
doOneSet();
}
You must write the function doOneSet which will, for now, write out 5 addition problems. All of the numbers printed should be between 0 and 100, inclusive. Here is a sample output for this phase:
45 + 21 =
0 + 100 =
54 + 23 =
4 + 19 =
18 + 92 =
The numbers that are produced for these problems must be generated randomly by the computer. The numbers in the sample output are given only as an example. Refer to lesson 7.3 for more information about generating
random numbers.
Phase 2: Change your doOneSet function so that instead of just writing out the problems it also allows the user to enter an answer, and then tells the user whether the answer was correct or not. Do not change your main()
function. Here is the sample output for this phase (user input is indicated here by using bold font. It won’t be bold in your own output):
45 + 21 = 66
correct
0 + 100 = 100
correct
54 + 23 = 67
incorrect
4 + 19 = 13
incorrect
18 + 92 = 110
correct
Before you move on to phase 3, refer to the structure diagram above and make sure that you have adhered to it for all of the functions indicated there for phase 2. This will probably mean dividing your doOneSet function up
into functions, if you haven’t done it already.
Phase 3: Now you will change your doOneSet function so that it will work for either addition, subtraction, or multiplication. For the purposes of this assignment, a set of problems is defined to be a group of problems that
are all of the same type (all addition, all subtraction, or all multiplication). After completing this phase your program will give 5 addition problems, 5 subtraction problems, and 5 multiplication problems, for a total of 15
problems. Your main() function must look exactly like this:
int main()
{
srand(static_cast(time(0)));
doOneSet(‘+’);
doOneSet(‘-‘);
doOneSet(‘*’);
}
The parameter tells doOneSet whether to do addition, subtraction, or multiplication. Notice that there is exactly one doOneSet function definition, not three!
At this point students often have trouble writing the parameter list for doOneSet. As a hint, remember that a parameter is simply a declaration statement, and that the number and type of a function’s arguments must match
the function’s parameters.
Here is the sample output for this phase:
45 + 21 = 66
correct
0 + 100 = 100
correct
54 + 23 = 67
incorrect
4 + 19 = 13
incorrect
18 + 92 = 110
correct
59 – 19 = 40
correct
19 – 84 = -29
incorrect
0 – 65 = -65
correct
96 – 1 = 95
correct
94 – 14 = 80
correct
0 * 87 = 0
correct
45 * 84 = 398
incorrect
8 * 37 = 873
incorrect
34 * 83 = 831
incorrect
38 * 3 = 238
incorrect
Phase 4: Now you are ready to let the user specify how many problems per set. (Recall that a set is a group of problems all of the same type. In this program we are doing three sets: one set of addition, one set of
subtraction, and one set of multiplication. This means that, for example, if the problems per set is 7, there will be a total of 21 problems given.) Ask the user to enter the number of problems per set at the very beginning of
the program, so that all three sets have the same number of problems per set. Now your main() function will look exactly like this except that you may add variable declarations in the indicated location:
int main()
{
}
srand(static_cast(time(0)));
getProbsPerSet(probsPerSet);
doOneSet(‘+’, probsPerSet);
doOneSet(‘-‘, probsPerSet);
doOneSet(‘*’, probsPerSet);
For this phase you should also add a header at the beginning of each set, as illustrated in the following sample output for this phase. For purposes of the header, you should assume that the addition problems will always be
set #1, the subtraction problems set #2, and the multiplication problems set #3.
Enter problems per set: 3
Set #1
———45 + 21 = 66
correct
0 + 100 = 100
correct
54 + 23 = 67
incorrect
Set #2
———59 – 19 = 40
correct
19 – 84 = -29
incorrect
0 – 65 = -65
correct
Set #3
———0 * 87 = 0
correct
45 * 84 = 398
incorrect
8 * 37 = 873
incorrect
Phase 5: Now let the user specify maxNum, the maximum number to be used for each set. This means that instead of choosing numbers between 0 and 100 (inclusive) for each problem, the computer will be choosing
numbers between 0 and maxNum (inclusive). You must allow the user to enter a different maximum number for each set. This won’t change the main() function, since you need to ask it again before each set. It will
be done near the beginning of your doOneSet function. Here’s the sample screen output:
Enter problems per set: 3
Set #1
———What is the maximum number for this set? 100
45 + 21 = 66
correct
0 + 100 = 100
correct
54 + 23 = 67
incorrect
Set #2
———What is the maximum number for this set? 90
59 – 19 = 40
correct
19 – 84 = -29
incorrect
0 – 65 = -65
correct
Set #3
———What is the maximum number for this set? 20
0 * 18 = 0
correct
15 * 4 = 398
incorrect
8 * 17 = 873
incorrect
Phase 6: Now you need to keep track of how the user is doing. after the user has attempted all of the problems, your program should write a report that says how many the user got right on each set out of how many and
for what percent. The report must also indicate the overall figures. Here’s a sample screen output:
Enter problems per set: 3
Set #1
———What is the maximum number for this set? 100
45 + 21 = 66
correct
0 + 100 = 100
correct
54 + 23 = 67
incorrect
Set #2
———What is the maximum number for this set? 90
59 – 19 = 40
correct
19 – 84 = -29
incorrect
0 – 65 = -65
correct
Set #3
———What is the maximum number for this set? 20
0 * 18 = 0
correct
15 * 4 = 398
incorrect
8 * 17 = 873
incorrect
Set#1:
Set#2:
Set#3:
Overall
You
You
You
you
got
got
got
got
2
2
1
5
correct
correct
correct
correct
out
out
out
out
of
of
of
of
3
3
3
9
for
for
for
for
66.7%
66.7%
33.3%
55.6%
Note that the results must be rounded to the nearest tenth. To round your results, just use
cout drawBox(width, height);
void getDimensions(int &width,
int &height)
{
–>cout > width;
cout > height;
}
Computer programmers, however, seldom think in terms of the actual value of memory addresses. A memory address is essentially just a way to refer back to that memory location. Instead of using the actual memory
address, we will use an arrow to point from the variable where the memory address is being stored to the memory location itself.
int main()
{
int width;
int height;
}
getDimensions(width, height);
–>drawBox(width, height);
void getDimensions(int &width,
int &height)
{
–>cout > width;
cout > height;
}
Now we have done the hard part: setting up the parameters. We are ready to start executing the statements inside the function getDimensions. The first statement doesn’t interest us here. The second statement does. Let’s
say that the user types the value 7 for the width. Where does the 7 go? C++ would normally put it in the local variable width, but when we look in width we see that it is not a normal variable but rather a “reference” to
another variable. So instead of putting the 7 in the local variable width, we follow the arrow back to the variable width which belongs to main and put the value 7 there. It is important to realize that the fact that the names
are the same is purely coincidental. C++ does not look at the names, rather it simply follows the arrow and uses the variable that the arrow is pointing to. After following the same procedure for the cin >> height;
statement, and assuming that the user enters a 4 for the height, our situation is this:
int main()
{
int width;
int height;
}
getDimensions(width, height);
–>drawBox(width, height);
void getDimensions(int &width,
int &height)
{
cout > width;
cout > height;
–>
}
Now we have reached the bottom of the getDimensions function. When we reach the bottom of a function, all of the variables declared in that function are deallocated — for now just think of them as going away. So when we
get back to main after having executed the getDimensions function, our situation looks like this:
int main()
{
int width;
int height;
}
getDimensions(width, height);
–>drawBox(width, height);
The rest of the program involves only pass-by-value. There are no more examples of pass-by-reference. We won’t trace through all of the rest of the program, but we will do a bit more for the sake of illustration.
The next statement to be executed is the call to drawBox. Two values are sent. The first value is the value stored in width (7), and the second value is the value stored in height (4). As we enter drawBox, two parameters are
declared: width and height. Since width is listed first, it gets initialized to the first value that was sent (7). Since height is listed second, it gets initialized to the second value that was sent (4). Notice that this has nothing to
do with the names of the arguments or parameters, but with the order in which they are listed. Here is the picture at this point:
int main()
{
int width;
int height;
}
–>
getDimensions(width, height);
drawBox(width, height);
void drawBox(int width, int height)
{
drawHorizontalLine(width);
draw2VerticalLines(width – 2,
height – 2);
drawHorizontalLine(width);
}
Now we are ready to execute the statements inside drawBox. When we call drawHorizontalLine, the value that will be sent is 7, since that is the value stored in width. When we call draw2VerticalLines, the values that will be
sent are 5 and 2, the values of width – 2 and height – 2, respectively. And so on.
I have not yet said anything about global variables. Global variables are variables that are declared outside of any function and that can, therefore, be accessed from any function. You should not use global variables, but you
should know how they behave in case you have to deal with someone else’s code that uses global variables. The rule is this. When a variable name occurs in any function (including main), C++ first looks to see if there is a
variable with that name that is local (i.e. declared inside that function). If so, that is the variable that is used. If not, C++ proceeds to look for a global variable with that name. If there is a global variable with that name, it
is used. If there is neither a local variable or a global variable with that name, a compile-time error results. One result of this procedure is that if there is a global variable AND a local variable with that name, it is the local
version that is used, not the global version
I have provided 6 trace programs. You can find links to these programs below. I strongly suggest that you print these out, try to predict the output, and then compile and run them to see if your answers are correct. You will
have to deal with global variables on a few of the trace programs I have provided.
Note about the trace problems: Some of the output from these trace programs will be “junk”, since the variables will be uninitialized. Be careful: sometimes this junk will be obviously junk (say you get a result like
-80343943). Other times, the junk might be a number like “0”, which looks like a real value that was computed, but is really just the value that happened to be in that variable.
trace
trace
trace
trace
trace
trace
example
example
example
example
example
example
1
2
3
4
5
6
Section 7.3: Generating Random Numbers
You will probably need to know how to generate random numbers to do at least one of your assignments in this class. This is done in C++ with the function rand(). This function is a value returning function, which means
that it is used as an expression when you call it. It returns a random integer between 0 and something very big (roughly 32,000). The following program illustrates the use of the rand() function and shows the results when
I ran the program. Notice that in order to use the rand() function I have to put a #include at the top of my program.
#include
#include
#include
using namespace std;
int main()
{
for (int i = 0; i < 100; i++){
cout