- (15 points) Declare a class named PgmImage that you could place in a .h file. Do not write the
method definitions as these would not go in a .h file. PgmImage should have the
following variables and methods:
- picture: a 2D vector of ints
- photographer: a string containing the name of the photographer
who took this picture.
- printPicture: a void method that takes a string parameter named
filename and writes the picture to that file.
- getPhotographer: a method that takes no parameters and
that returns the photographer's name.
- crop: a void method that crops the picture. This method takes
the following parameters:
- croppedImage: a 2D vector of ints. The cropped image will be placed in this
2D vector
- row, col, numRows, numCols: 4 ints specifying the
cropped rectangle. The cropped rectangle starts at (row, col)
in the picture and includes numRows rows and numCols columns.
class PgmImage {
public:
void printPicture(string filename);
string getPhotographer;
void crop(vector<vector<int> > &croppedImage, int num, int col, int numRows, int numCols);
protected:
vector<vector<int> > picture;
string photographer;
};
- (12 points) Suppose I want to declare a class named PgmImage
and use it in a program named ImageManipulator.cpp. I also want to
implement the methods for PgmImage in a separate file from ImageManipulator.cpp.
Answer the following questions:
- If the class declaration for PgmImage is placed in a file
named PgmImage.h, then what statement would you write
to include this header file in .cpp files?
PgmImage.h: The class declaration is placed in a header file with the
name of the class and a .h suffix. The .h suffix indicates that the file is
a header file.
- In what file should the method definitions for PgmImage be placed?
Give the name of the file, following the naming conventions discussed
in class.
PgmImage.cpp: The method definitions are placed in their own .cpp file
with the name of the class as the name of the file.
- Using a single g++ statement, how could I compile all of these files into an
executable named ImageManipulator?
g++ -o ImageManipulator ImageManipulator.cpp PgmImage.cpp
- Suppose instead that I wanted to separately compile these files and
then link together their object files into an executable named
ImageManipulator. Write the set of statements that would be required
to perform this separate compilation and then linking into the
executable.
g++ -c ImageManipulator.cpp
g++ -c PgmImage.cpp
g++ -o ImageManipulator ImageManipulator.o PgmImage.o
- Suppose that I execute ImageManipulator
from the command
line by writing the following command:
./ImageManipulator < bvz.pgm
What does the < operator cause the C++ program to do?
It causes cin to read input from bvz.pgm rather than from the console. Another way of putting it is that it causes stdin to be re-directed from the console to bvz.pgm.
- (8 points) In a function, why should you use reference parameters for objects
(e.g., strings, vectors, instances of classes),
rather than value parameters, even if you do not intend to modify the object? For example, if you want to pass a vector
object to a function, you should declare the vector parameter as follows:
void mystery(string &vector) { ... }
Straight from the procedures notes: You want the convenience of using a
procedure, but you don't want to make copies, because that is expensive.
Here is a slightly wordier explanation:
A reference parameter only copies a pointer to an object, which is only
4-8 bytes, whereas a value
parameter would copy the contents of the entire object. If the object is
quite big, such as a lengthy string or a vector with a large number of
elements, then copying the contents of the entire object could slow down
the program considerably.
- (10 points) Answer the following two questions about typedefs:
- Write a typedef statement that declares a new type named Pixels to be a vector of ints.
typedef vector<int> Pixels;
- Declare picture to be a vector of Pixels.
vector<Pixels> picture;
- (24 points)
- Insert the keys 22, 28, 39, 48, 41, and 61 into
the following two hash tables using a) separate chaining, and b) quadratic
probing. For separate chaining, use commas to separate the keys that
would go into an entry. For example, if the keys 22, 28, and 39 all
went into the same entry, you would write (22, 28, 39) in that entry.
You should use the hash function:
h(key) = key % 13
| Separate Chaining | Quadratic Probing |
0 | 39 | 39 |
1 | | |
2 | 28, 41 | 28 |
3 | | 41 |
4 | | |
5 | | 61 |
6 | | |
7 | | |
8 | | |
9 | 22, 48, 61 | 22 |
10 | | 48 |
11 | | |
12 | | |
-
For quadratic probing
please also complete the following table showing the comma, separated list of entries you
would probe in order to insert each key (e.g., if you examined entries 6, 7, and 11 in order to insert 48, then you would write (6, 7, 11) next to 48
in the below table--this is only an example--it is not the actual set
of entries you will probe for 48). This will
allow us to assign you partial credit if you do not get the entries
correct in the quadratic probing hash table.
key | quadratic probing |
22 | 9 |
28 | 2 |
39 | 0 |
48 | 9, 10 |
41 | 2, 3 |
61 | 9, 10, 0, 5 |
Separate chaining
- 22: 22%13 = 9 so 22 is added to the vector associated with entry 9
- 28: 28%13 = 2 so 28 is added to the vector assocaited with entry 2
- 39: 39%13 = 0 so 39 is added to the vector assocaited with entry 0
- 48: 48%13 = 9 so 48 is added to the vector assocaited with entry 9
- 41: 41%13 = 2 so 41 is added to the vector assocaited with entry 2
- 61: 61%13 = 9 so 61 is added to the vector assocaited with entry 9
Quadratic Probing:
- 22: 22%13 = 9. Entry 9 is empty so we place 22 in entry 9.
- 28: 28%13 = 2. Entry 2 is empty so we place 28 in entry 2.
- 39: 39%13 = 0. Entry 0 is empty so we place 39 in entry 0.
- 48: 48%13 = 9. Entry 9 is full. We next query the following entries:
- 10: hash(48) = (9 + 12) % 13 = 10
Entry 10 is empty so we place 48 in entry 10.
- 41: 41%13 = 2. Entry 2 is full. We next query the following entries:
- 3: hash(41) = (2 + 12) % 13 = 3
- 61: 61%13 = 9. Entry 9 is full. We next query the following entries:
- 10: hash(61) = (9 + 12) % 13 = 10
- 0: hash(61) = (9 + 22) % 13 = 13 % 13 = 0
- 5: hash(61) = (9 + 32) % 13 = 18 % 13 = 5
- (12 points) The intent of the following
code fragment is to read a series of name/salary
pairs from stdin and to print the average salary to stdout.
1 string name;
2 int salary;
3 int salarySum = 0;
4 while (!cin.eof()) {
5 cin >> name >> salary;
6 salarySum += salary;
7 }
8 cout << "total salary = " << salarySum << endl;
- Show what the code fragment prints if the input is:
Suzy 100
Frank 80
Ebber 50
total salary = 280
- What is wrong with the code that causes the output to be incorrect?
cin.eof() is reactive, not proactive. That means that when the last
name/salary pair is read, cin.eof() will still return false and the
loop will execute one too many times. The cin on line 5 will fail and leave
name and salary unchanged, which will result in the last salary being added
to salarySum twice.
- Modify the incorrect portion of the code to make it correct. Please do
not re-gurgitate the entire code. Only modify the incorrect portion.
The correction is to replace the condition
!cin.eof() with the condition cin >> name >> salary and
to delete line 5:
while (cin >> name >> salary) {
salarySum += salary;
}
- (8 points) The following code fragment is supposed to read names into a vector.
1 vector<string> names;
2 int i = 0;
3 string name;
4 while (cin >> name) {
5 names[i] = name;
6 i++;
7 }
- What is wrong with the code? (you may use no more than 4 sentences)
The names vector has size 0 so when you try to assign
the variable name to names[i], the code will seg fault because
entry i does not exist.
- Change the incorrect portion of the code to make it correct. Do not
regurgitate the entire code. Only modify the incorrect code fragment.
The solution is to replace the assignment statement with a call
to push_back, which will allocate a new entry in the vector
and then assign name to that new entry. You should also delete the
i++ statement since there is no longer a need to keep track
of which entry you are assigning name to:
while (cin >> name) {
names.push_back(name);
}
Technically you should also delete the declaration for i since
you no longer need it, but I did not deduct points for failing to
do so.
- (12 points) Please read the following instructions before attempting this problem:
- For part (a) convert the hexadecimal
number to binary (0's and 1's).
- For parts (b)-(d) give the hexadecimal result of the operation. Assume that there is only 8 bits in which to store the result (this is only relevant for part d). For these three parts, if you cannot figure out the hexadecimal representation of the answer you can get 2 points for each part by giving the binary representation of the answer.
- You can show your work in the blank spaces to
get partial credit:
- 0xB5 = 1011 0101
- (0101 1101) ^ (1110 1100) = 1011 0001 = 0xB1
^ is bitwise xor
- (0010 0101) | (1010 1100) = 1010 1101 = 0xAD
| is bitwise or
- (1010 1100) << 3 = 0110 0000 = 0x60
<< left shifts the bits by 3 positions, which means that
the leading 101 on the left side gets thrown into the bit bucket
and three 0's come in on the right side.
- (5 points) g What is the output of the following printf statement:
char value = 'a' + 6;
printf("%c", value);
6 is being added to the ascii code for 'a',
which is 97. The sum is 103, which is the ascii code for 'g'.
In plain English, we are counting 6 characters from 'a',which gives you:
1 2 3 4 5 6
b c d e f g
- (14 points) Suppose you are given the following variable declarations:
string runner;
int minutes, seconds;
Write a printf statement that:
- outputs runner in a left-justified field of 15 characters, and time
in the following two formats: a) as a right-justified floating point number in a
field that is minimally 6 characters wide with 1 decimal digit where the whole number is the minutes and the fractional number is the seconds divided by 60, and b) as a right-justified string in the format mm:ss (i.e., both minutes and seconds should be in fields that are 2 characters wide).
- If the number of seconds is less than 10, pads the seconds output with
a single 0, and
- Puts a single space between each of the three fields (but no space at
the end of the output).
For example, if the runner's name is "Bradley", minutes is
5, and seconds is 40, then the output would be:
Bradley 5.7 5:40
(40/60 = .67 which when rounded to one digit is .7) and if seconds were instead 4, then the output would be:
Bradley 5.1 5:04
printf("%-15s %6.1f %2d:%02d\n", runner.c_str(),
minutes + (double)seconds/60, minutes, seconds);
- (Extra Credit-12 points) You have written a program that reads the exam scores for a student
and for each student prints the student's name and exam average.
Your program reads lines of input where the first
and second words on the line are a student's first and last name and the
remaining portion of the line are scores for the student. For example:
Donald Duck 20 40 50
All exam scores must be integers between 0 and 100 and every student
must have at least one exam score. A line of input must therefore have
at least 3 inputs--a firstname, lastname, and test score.
When you test your program, you need to come up with a series of test
cases for correct input, incorrect input, and boundary cases. List
all the test cases you should provide in order to thoroughly test your
program (this is what you are supposed to do in each of your labs). I
will get you started with a couple of example test cases:
Non-erroneous input: This will include all "normal" data plus
all boundary cases.
- A line with a single test score--this represents the minimum
possible number of test scores
- A test score of 0. This is the smallest valid test score.
- A test score of 100. This is the largest valid test score.
Erroneous input: A test case with:
- An exam score that is negative.
- An exam score that is greater than 100.
- An exam score that is not an integer.