Implementing IntList, Phase 4: The Finishing
In this part, we'll add the finishing touches to the IntList class.
The Interface
The functions we need to implement are:
- (copy constructor)
- Makes a duplicate copy of another
IntList. - (assignment operator)
- Replaces the contents of this list with a duplicate copy of another
IntList. - (equality operator)
- Checks if two
IntListobjects are equal. - (inequality operator)
- Checks if two
IntListobjects are not equal.
All these functions are \( \mathrm{O}(n) \), where \( n \) is the number of elements in the list. Copying and assignment are \( \Theta(n) \), because we have to copy each element. Equality and inequality are \( \Theta(n) \) in the worst case because we may have to compare each element, but they are \( O(1) \) in the best case because we may be able to determine that the lists are unequal by just looking at their sizes or by finding a mismatch on the first element.
Your Task, Part 1: Planning the Implementation
Although you should plan out these functions for yourselves, we'll give some strong hints about how to implement them.
You may also want revisit this lesson to remind yourselves about implementing copy constructors and assignment operators for classes that manage their own dynamic memory.
Copy Constructor
Use a loop to iterate through the other list, using push_back to add each element to the new list.
Assignment Operator
Use the copy-and-swap idiom. Make a copy of the other list in a temporary variable, then swap it with the current list. The temporary variable will then go out of scope and its destructor will clean up the old contents of the current list.
Equality Operator
First, check to see if the sizes are different. If they are, the lists are not equal and you're done. If the sizes aren't different, use a loop to compare each element.
Inequality Operator
This operator can be implemented in terms of the equality operator. If the equality operator returns false, then the lists are not equal.
Planning Evidence
Although you're welcome to plan things out on paper, you don't need to submit any planning evidence for this part of the assignment.
Meh. I'll skip the planning.
Just because we say you don't need to submit planning evidence doesn't mean you don't have to think about how you're going to implement these functions. If the descriptions above seem completely clear to you, then that's fine, but if anything seem unclear, you should definitely take some time to think about it before you start coding.
Your Task, Part 2: Adding the Declarations
First, remove the = delete lines in intlist.hpp that disable copying and assignment. Then, add the following declarations, but include docstring comments describing what each function does, its parameters, return value, rules for usage, and complexity.
IntList(const IntList& other);
IntList& operator=(const IntList& other);
bool operator==(const IntList& other) const;
bool operator!=(const IntList& other) const;
You can check to be sure that you haven't introduced any syntax errors in the header by running make intlist.o.
Your Task, Part 3: Writing Test Cases for the New Functions
In intlist-test.cpp, extend your test cases to test the new functions. Think about the edge cases you need to test, especially when the list has only one element or none.
You can check your code for syntax errors by running make intlist-test.o. Obviously, you can't test against IntList yet, since you haven't implemented these functions, but at least you can check for serious issues with the tests. (If you disable your tests for insert_after, you could check your tests against IntVector, but that's not required and probably not a good use of your time.)
Your Task, Part 4: Implementing and Testing the New Functions
Now that you have a plan and test cases, it's time to implement the new functions in intlist.cpp. If your new functions only call preexisting functions, you don't need to add a call to check_invariants() (because they already call it), but if you write any new code that modifies the list's data members directly (why?), make sure you add a call to check_invariants() at the end of the function, to catch any accidental violations of invariants.
Once they're written, run your test cases to check that they work correctly.
cs70-make -H intlist-test
./intlist-test
As usual, if things fail, you can use valgrind to help you find the problem and all the usual debugging advice applies.
Okay, we've built a pretty general singly linked list class. We can use it like a stack or a queue, iterate through it, insert elements in the middle, copy it, assign it, compare it… Nice.
I'm happy!
I'm not seeing anything missing.
Meh. I think it was all pointless. C++ already has
std::listwhich does all this and more.
Well, yes, but
std::listis a doubly linked list, which is more complicated than what we need for the Snake game.
Meh. There's not going to be a performance difference. It's all just constant-time operations. So they'll be the same, right?
Not quite. An asymptotic perspective ignores constant factors that matter in real code. A doubly linked list uses more memory per node and has more complex code, both of which might lower performance. The only to answer your question, Goat, is for us to conduct some experiments.
Bah! I wasn't asking for more work!!!
MORE to learn about! Oh YEAH!!!
Doing some actual science in computer science! Woo-hoo! I'll fetch my labrador coat!
(When logged in, completion status appears here.)