CS 70

Implement Car Member Functions

In this part of the assignment, you will implement the functionality of the Car class by filling in the stubs in the car.cppfile that you created earlier.

We are going to model the need to switch to trains of different sizes, and that can change over the course of a program run, Cars will need to be dynamically allocated on the heap.

We will represent an empty Car with capacity 4 using a picture:

[_][_][_][_]~

We will represent a Car with capacity 4, holding 2 packages, using a picture like

[x][x][_][_]~

Note that bins should always be filled in from left to right.

A full car with capacity 4:

[x][x][x][x]~

Your Tasks

Implement the Car Default Constructor

Your Car constructor should

  • Initialize the value of binsInUse_ to be 0.
  • Iterate over bins_ and set them all to false, indicating that the car’s bins are all empty.

Implement Functions That Describe Bin Status

  • LHS Cow speaking

    After implementing each function, it's a good idea to run make (or cs70-make) to make sure your code compiles correctly and without warnings.

Always Scroll Up to the First Error

Sometimes, especially for code involving <<, the compiler can give super voluminous errors. Just scroll up to see the first thing that went wrong. Often the errors are long because once the compiler becomes confused, it's unable to correctly interpret the rest of your code. But sometimes just one error can produce tons of output, because the compiler is saying, “I couldn't make sense of your code; here's everything I tried and none of these interpretations worked”. For example, there are numerous versions of operator<< for different types, and when you give << something it can't print, it tells you all the things it can print as part of the error, which ends up being a massive number of things.

Car::getUsage()

  • Return the current value of binsInUse_.

Car::isFull()

  • Return true if and only if the number of bins in use is equal to the car capacity. Otherwise it should return false.

Car::isEmpty()

  • Return true if and only if the number of bins in use is equal to zero. Otherwise it should return false.

Functions That Return a bool

As noted in the help page on coding idioms, avoid unnecessary conditional statements that end with code like return true and return false.

Implement Functions to Add and Remove Packages

  • RHS Cow speaking

    Running make or cs70-make after you've coded each function will make it easier to narrow down (and fix!) any errors or warnings you get from the compiler—if the only new code is the code you're working on right now, you can have a pretty good idea where to start looking.

Car::addPackage()

  • Set the first false position of bins_ to be true, simulating the addition of a package. This should also increment the value of binsInUse_.

    • Hint: To find the location of the first false value, do you actually need to write a loop? How could the value of binsInUse_ help?
    • Note that people are not allowed to call addPackage() when a car is full.
  • Hedgehog speaking

    What should we do if people break the rules and try to add a package to a full car?

  • LHS Cow speaking

    People aren't supposed to break the rules. If they do, the result is “undefined behavior”.

  • Hedgehog speaking

    Okay, sure, but what should I do for my “undefined behavior”?

  • LHS Cow speaking

    It's up to you. We recommend one of three choices:

    • Don't check. Whatever happens, happens; probably an array out-of-bounds access will occur. Not checking maximizes execution speed, but causes usage errors to go undetected. On the positive side, if you don't write any code to check for improper use, the checking code can't be buggy.
    • Check isFull() and throw a std::length_error exception if needed. This approach detects improper usage at the cost of a few nanoseconds of execution time. (Requires #include <stdexcept>.)
    • Use an assertion. In this case, the assertion would be

      assert(!isFull());
      

      This code will abort the program with an error message if the condition !isFull() is false. This approach has the advantage that it's just one line of code, and if people need maximum speed in production code, checking of assertions can be turned off with a compiler option. (Requires #include <cassert>.)

Car::removePackage()

  • Set the last true position of bins_ to be false, simulating the removal of a package. This function should also decrement the value of binsInUse_.

    • Hint: Similar to above, to find the location of the last true value, do you actually need to write a loop?
    • Note that people are not allowed to call removePackage() when a car is empty.
  • Duck speaking

    If anyone breaks the rules, I'll erase all their files! Undefined behavior FTW!

  • LHS Cow speaking

    No, we don't do things like that.

  • Duck speaking

    Yeah, but I do. So look out, rulebreakers!

Implement Printing for Car Objects

operator<<

You already implemented operator<< for the Car class in Part 3 so there should be nothing to do for this function.

  • Horse speaking

    Hold your horses, I still don't get it. What's the deal with operator<<? Why does it return an ostream& and why is it always the same one that was passed in? What use is that?

  • LHS Cow speaking

    Okay, let's go through it one more time.

Review: The << Operator

When we write

cout << "My car is: " << myCar << endl;

it's the same as writing

((cout << "My car is: ") << myCar) << endl;
// ^^^^^^^^^^^^^^^^^^^^ ----------------------- this expression returns cout
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------------ and this one does, too

And that code has the same result as writing

cout << "My car is: ";
cout << myCar;
cout << endl;

which explains why implementations of operator<< for output streams always return the output stream. Our implementations of operator<< just call our printToStream member function, so our original code,

cout << "My car is: " << myCar << endl;

ends up being the equivalent of

cout << "My car is: ";
myCar.printToStream(cout);
cout << endl;

printToStream

Now you need to actually implement printToStream. It will output a text representation of the Car to the provided stream.

First, replace your stub code with something like

void Car::printToStream(std::ostream& outStream) const {
    outStream << "[mystery car]";
}

and confirm everything still compiles.

Now, replace that dummy implementation that outputs [mystery car] to the output stream with the real code. Here's pseudocode for what needs to be done:

  • loop over bins_
    • if the bin is occupied
      • output [x]
    • if the bin is not occupied
      • output [_]
  • output a ~ to represent the connector to the next car
  • DO NOT output a newline/endl!

Thus a car with three occupied bins followed by one unoccupied one would send [x][x][x][_]~ to the output stream.

Remember, all output is sent to outStream, not cout.

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.)