ICS 45C Spring 2022
Exercise Set 6

Due date and time: Wednesday, May 18, 11:59pm


Getting started

First of all, be sure that you've read the Reinforcement Exercises page, which explains everything you'll need to know, generally, about how the reinforcement exercises will work this quarter. Make sure you read that page before you continue with this one.

Before you begin work on these reinforcement exercises, there's a chore that you'll need to complete on your ICS 45C VM to get it set up to proceed.

Refreshing your ICS 45C VM environment

Even if you previously downloaded your ICS 45C VM, you will probably need to refresh its environment before proceeding with these exercises. Log into your VM and issue the command ics45c version to see what version of the ICS 45C environment you currently have stored on your VM. Note, in particular, the timestamp; if you see a version with a timestamp older than the one listed below, you'll need to refresh your environment by running the command ics45c refresh to download the latest one before you proceed with these exercises.

2022-05-12 07:15:15
Exercise Set 6 template added

If you're unable to get outgoing network access to work on the ICS 45C VM — something that afflicts a handful of students each quarter — then the ics45c refresh command won't work, but an alternative approach is to download the latest environment from the link below, then to upload the file on to your ICS 45C VM using SCP. (See the Project #0 write-up for more details on using SCP.) Once the file is on your VM, you can run the command ics45c refresh_local NAME_OF_ENVIRONMENT_FILE, replacing NAME_OF_ENVIRONMENT_FILE with the name of the file you uploaded; note that you'd need to be in the same directory where the file is when you run the command.

Creating your project directory on your ICS 45C VM

A project template has been created specifically for this set of exercises, containing a similar structure to the basic template you saw in Project #0. Among other things, it contains a version of the gather script that's different from the ones in the projects, so you'll absolutely need to use the set6 template for these exercises, as opposed to the basic one.

Decide on a name for your project directory, then issue the command ics45c start YOUR_CHOSEN_PROJECT_NAME set6 to create your new project directory using the set6 template. (For example, if you wanted to call your project directory set6, you would issue the command ics45c start set6 set6.)

A word about organizing your answers

Please do not copy and paste the problem (or portions of the problem) into the answer you submit. We are well aware of what question you're asking, so copying and pasting the problem simply slows us down when grading your work. Consequently, we will be deducting points when problems are copied and pasted into solutions.


Problem 1 (2 points)

We saw that the C++ Standard Library provides iterators, but it's not uncommon for students to find themselves unclear on what problem iterators are meant to solve. Let's explore that in this problem.

  1. Suppose you had a std::vector<int> called numbers. Write a classic for loop — so, not a range-based for loop — that uses an iterator to determine the sum of the integers in the vector. Use only the iterator to obtain the various integers in the vector.
  2. Now suppose you instead had a std::list<int> called numbers. Similarly, write a classic for loop that uses an iterator to determine the sum of the integers in the list. How did your loop look different than the one you wrote for the std::vector? If it looked different, is there a way to rewrite it so that it would be identical in both cases? If so, make the adjustment so they're identical; if not, explain why they can't be made identical.

What to submit

Add one PDF file to your problems directory with this name: problem1.pdf, which contains your answers to these questions.


Problem 2 (4 points)

In your problems directory, you'll find a C++ header file called problem2.hpp that declares the two functions you'll be writing in this problem. Leave this file as-is, then create a new C++ source file called problem2.cpp where you'll implement the two functions.

First, write a C++ function with the following signature:

std::map<std::string, unsigned int> countWordFrequencies(const std::vector<std::string>& words)

Its input is a std::vector containing a sequence of words. Its output should be a std::map with string keys and unsigned integer values, where the keys are the words that appear at least once in the vector and the values are the number of times each word appears in the vector.

Then, write another C++ function with the following signature:

std::string formatWordFrequencies(const std::map<std::string, unsigned int>& frequencies)

Its input is a std::map with the same meaning as the result from countWordFrequencies and its job is to return a std::string with that information formatted in a two-column format like this.

BOO        117
HELLO       36
PERFECT      9
PERFECTLY   14
QUICK       20

There are a couple of requirements that must be met by this formatting:

When you're done with your functions, it would be a good idea to test them, which you can do either by writing code in app/main.cpp that calls into your functions or by writing unit tests in the gtest directory. (One good reason why we've designed formatWordFrequencies to return a std::string rather than just printing to std::cout is to facilitate unit testing. Functions with side effects are tricky to test, so it's better if we can limit how many of our functions actually have side effects; printing formatted output is better designed as one function to do the formatting and another to do the printing.) The presence of the problem2.hpp file in the problems directory will make your functions available either in app or gtest, as long as you include problem2.hpp in any source file that uses them.

What to submit

Add one C++ source file to your problems directory with this name: problem2.cpp, which contains the definition of your two functions.


Problem 3 (2 points)

In our discussion about the C++ Standard Library, we introduced you to the auto keyword and how it can be used to reduce the complexity in writing some type declarations. When students first encounter the auto keyword, they often see its flexibility and naturally wonder how flexible it actually is.

We saw that auto does not provide us a way to have variables whose types change at run time; it is purely a compile-time mechanism. Why is this limitation in place in C++? If auto did mean "This variable can store a value of any type," why would that be problematic?

What to submit

Add one PDF file to your problems directory with this name: problem3.pdf, which contains your answers to this question.


Problem 4 (2 points)

When the author of a derived class intends to override a member function inherited from a base class, the author has the option of adding an override specifier to the member function's signature in the derived class. As we saw when we talked about Inheritance and Polymorphism, this is not required, but is a good idea. Let's explore why by experimenting with it.

First, in the problems directory, write a class containing at least one virtual function and at least one non-virtual member function. Name your class anything you'd like, but for any name X you choose, the name of your files should be X.hpp and X.cpp. (To avoid confusion, don't include the word problem, regardless of capitalization, in the name of your class.) Name its member functions anything you'd like, as well, but do implement something in them that lets you understand whether they're behaving the way you expect (e.g., have them return a constant value).

Next, in the problems directory, write another class that inherits from your class, then try the following things and see which ones work as you'd expect, which ones compile but don't work as you'd expect (and why), and which ones fail to compile (and why). Explain, for each of these, how it behaved on the ICS 45C VM specifically when you wrote your derived class this way and experimented with it in exp/expmain.cpp. (Each of these is a separate scenario, i.e., you wouldn't keep adding to your derived class each time.) Whenever you override a member function from the base class, be sure it does something different, so you can tell the override apart from the inherited version.

  1. In your derived class, override the virtual function from your base class without either the words virtual or override in its signature.
  2. In your derived class, override the virtual function from your base class with the word virtual but without the word override in its signature.
  3. In your derived class, override the virtual function from your base class with the word override but without the word virtual in its signature.
  4. In your derived class, override the virtual function from your base class with the word virtual but without the word override in its signature, but with the name of the function misspelled.
  5. In your derived class, override the virtual function from your base class with the word override but with the name of the function misspelled.
  6. Same as #1, but for the non-virtual member function from your base class.
  7. Same as #2, but for the non-virtual member function from your base class.
  8. Same as #3, but for the non-virtual member function from your base class.
  9. Same as #4, but for the non-virtual member function from your base class.
  10. Same as #5, but for the non-virtual member function from your base class.

What to submit

Add one PDF file to your problems directory with this name: problem4.pdf. We will not need the C++ header and source files for your base and derived classes, so you can implement those anywhere you'd like.


Deliverables

In Canvas, you'll find a separate submission area for each problem. Submit your solution to each problem into the appropriate submission area. Be sure that you're submitting the correct file into the correct area (i.e., submitting your Problem 1 solution to the area for Problem 1, and so on). Under no circumstances will we offer credit for files submited in the incorrect area.

Submit each file as-is, without putting it into a .tar.gz file or arranging it in any other way. (There is a gather script in the set6 template, but there's no need to use it.) If we asked for a PDF, for example, all we want is a PDF; no more, no less. If you submit something other than what we asked for, we will not be offering you any credit on the submission. There are no exceptions to this rule.

Of course, you should also be aware that you're responsible for submitting precisely the version of your work that you want graded. We won't regrade an exercise simply because you submitted the wrong version accidentally, and we won't be able to offer any credit on exercises that you forgot to submit.

Can I submit after the deadline?

Unlike some of the projects in this course, the reinforcement exercises cannot be submitted after the deadline; there is no late policy for these. Each is worth only 3% of your grade (and the lowest score doesn't count), so it's not a disaster if you miss one of them along the way.

What do I do if Canvas slightly adjusts my filename?

Canvas will sometimes modify your filenames when you submit them (e.g., when you submit the same file twice, it will change the name of your second submission to end in -1.tar.gz instead of just .tar.gz). In general, this is fine; as long as the file you submitted has the correct name, we'll be able to obtain it with that same name, even if Canvas adjusts it.

Can I work on this outside of the VM?

Yes, but be aware that you must submit the files in the appropriate format, and that any C++ code you submit must compile and run on the ICS 45C VM.