CS 70

Phase 2

In this step, you will plan and implement the following member function(s) of the IntList class:

  1. push_front(…)
  2. pop_front()
  3. The destructor

When it comes to testing, you will now also be able to expand test coverage for the size() and empty() functions.

In this phase of development, you will have to do all four steps that we described in detail in the previous phase (i.e., plan, write tests, implement, test).

Steps

  • LHS Cow speaking

    We've included some hints for each step.

  • RHS Cow speaking

    Be sure to read them before you do each step!

  • LHS Cow speaking

    Also, it's very tempting to just skip planning and dive into coding, but drawing a diagram can really help you figure out what the important cases are, and where the data you need is stored (and how to access it).

1. Plan

Plan what the functions listed in the Overview above should do. Upload your planning image as Planning/Phase_2.jpg.

Upload Your Planning Images with the Right Names and Formats

To get credit for your images, you need to provide your picture files in the right format and with the exact names that we (really the autograder) are expecting, including capitalization, spacing, file extension, and so on. You also need to put the file in the correct directory and git add, git commit, git push to make sure the file(s) end up in your GitHub repo.

Although we haven't tracked down exactly how it occurs, sometimes when students upload images from the command line with git, the images don't get uploaded properly. So it's important that you check your images in the GitHub web interface to make sure they're there and that you can view them. You can also upload images directly using the GitHub web interface rather than using the command line.

2. Write Tests

Add one or more testing functions to intlist-test.cpp to test all the functions you're writing in this part.

Testing Suggestions

  • Cat speaking

    Should I write one test for push_front() and another separate test for pop_front(), or just a single test that checks both?

  • LHS Cow speaking

    It's up to you.

You can't test all of push_front()'s behavior until pop_front() is also working, but you can test some of it. Being able to test push_front() (a bit) before you've even written pop_front() is (arguably) a good thing.

But push_front() and pop_front() clearly have a relationship, so testing them together makes sense.

  • Sheep speaking

    Would everyone be okay with this sort of idea for a test of both?

    • Small loop pushing some ints (101, 102, 103, etc.) into the list. On each iteration, check size() and empty().
    • Small loop popping all those ints off the list. On each iteration, check that we get the right values.
    • When we're done, be sure that the list is back to being empty.
  • LHS Cow speaking

    Sounds great.

  • Pig speaking

    What about MORE loop iterations? How about a million ints?

  • LHS Cow speaking

    It's pretty unlikely that a bigger loop will catch any more bugs than the smaller one.


Don't Test Improper Use

  • Duck speaking

    Can I test pop_front() on an empty list? My code throws an exception, so I can see if that happens.

  • LHS Cow speaking

    Noooo!

Users are never supposed to call pop_front() on an empty list. If they break this rule, they get undefined behavior.

A different team might have put assert(!empty()); at the top of pop_front(), to check its precondition, and assert just crashes the program if the assertion fails.

  • Goat speaking

    And I don't have any checks in pop_front(). Less code! If someone breaks the rules, they get what they get.

  • LHS Cow speaking

    Exactly. That's totally allowed.

You Can't Explicitly Test the Destructor

If your destructor doesn't work properly, you'll see problems with memory leaks running under Valgrind. But if you've coded it as we suggest, the code should be so simple that it doesn't need much testing anyway.

Double Check Your Tests

When writing tests, make sure you call summarize at the end of the function and return the result!

Also remember to add the appropriate function call(s) to the main function in intlist-test.cpp, so your tests actually get run.

3. Implement

Implement the functions listed above. Write them in the order listed.

Notes

new and delete

A good rule of thumb is “when you write new, figure out where the delete should go”. If your code for push_front creates a new list node with new Node{val, ptr}, where should you be putting a corresponding delete to mirror that new?

All Nodes are on the Heap

Don't create Node objects on the stack. When you make Nodes, you need to be using new.

  • Dog speaking

    Can I create a Node on the stack and then use the address-of operator (&) to get a pointer to it?

  • LHS Cow speaking

    What will happen to that object when the function returns?

  • Dog speaking

    Uh… It'll go away. And I'll be left with a dangling pointer into the stack. That'd be bad. Actually, I did get some compiler warnings about that.

  • LHS Cow speaking

    Don't ignore compiler warnings!

Writing the Destructor

In general, a good way to code is to try to avoid writing “tricky” code. The job of the destructor is to get rid of all the data in the list. You could write an explicit loop that calls delete, but doing that gives you two places where you have a delete and only one with a new, which is a bit asymmetric.

Instead, it's better to just use the code you have. Your job is to throw everything in the list away, and you can just pop things off the list until it's empty. The code for doing that should be very simple and thus obviously correct.

4. Test and Fix

Run all your tests and fix any bugs.

To Complete This Part of the Assignment…

You'll know you're done with this part of the assignment when you've done all of the following:

(When logged in, completion status appears here.)