Modeling Quinn's Train in C++
We are going to model Quinn's train as a C++ class called Train
. A train will have a dynamically allocated array of (train) cars, which we will model as a class called Car
.
“Creativity is always a leap of faith. You’re faced with a blank page, blank easel, or an empty stage.” —Julia Cameron
“No matter how many technologies might be developed to augment the operation of our bodies and mind, it is hard to imagine a better starting point for thought than a blank sheet of paper. Its honesty and reliability naturally provoke excitement for the next encounter.” —John Maeda
In previous assignments, you've had extensive starter code to work with; it's often the case that you'll find yourself working with an existing codebase, and knowing how to figure out how things work and how to implement your own functionality in the same style and approach is a very important skill to have and to practice.
But in this assignment, the only code we've given you is testtrain.cpp
, for testing your code once it's all written and working. The rest of the code—car.hpp
, car.cpp
, train.hpp
, train.cpp
—is up to you to write from scratch. (Also a very useful skill.)
Feel free to look at examples of C++ classes from previous lessons or homework assignments that you can use as examples to help you code!
Implementation Steps
Create the Car
Header File
Your first step is to create the header file that contains the definition of your Car
class. (In the next part of this assignment you will write the implementation file that contains the code defining the functions that can be called by a Car
.)
Setting up car.hpp
Create a new file named car.hpp
. For now, we are only defining the data members and member functions that a Car
has; that is, we are focusing just on the interface.
Inside car.hpp
, complete the following tasks:
- You must use an include guard to prevent the
Car
class from being defined twice if it is included in multiple files. - You should include
<ostream>
and<cstddef>
(which declaressize_t
). - Declare the class
Car
. (Don’t forget the semicolon after the closing curly brace of the class definition!)
Next you’ll specify all the data members and functions. We're not writing the implementation code for these functions yet, just declaring them to say what they are and what they'll do. We'll start writing the code in the implementation file, car.cpp
, in the next part of the assignment.
Declare the Data Members
Each car will have bins that can each store one package, which we will model as a Boolean array, bins_
, with a constant size, CAPACITY
, where bins[i]
is true
if there is a package stored in bin number i
and false
otherwise. Thus,
- Constant:
CAPACITY
, set to 4.- This says that a car will have space for four packages.
- You define a class-wide constant by declaring it as
static constexpr
inside the class.- It should be
static
so that the same constant is shared by all objects of the class. - It should be
constexpr
so that it is a constant known at compile time.
- It should be
- It should be
public
so that it can be accessed outside the class. - Inside the
Car
class, you can access this value as justCAPACITY
. - Outside the
Car
class, you can access it asCar::CAPACITY
.
- Member variable:
bins_
, an array ofbool
s.- The array should have
CAPACITY
elements. - It should be
private
so that it can only be accessed by member functions of the class. - Because this is a fixed size, known at compile time, you should not dynamically allocate it (i.e., no need for pointers or
new
). Just declare it as a regular array.
- The array should have
- Member variable:
binsInUse_
, asize_t
.- This variable will keep track of how many bins are occupied in a given car.
- It should be
private
so that it can only be accessed by member functions of the class.
Declare the Member Functions
- Your
Car
class should explicitly disable- The copy constructor
Car(const Car& other)
. - The assignment operator
Car& operator=(const Car& other)
.
- The copy constructor
- Your
Car
class should state that it is using the synthesized destructor~Car()
. - Your
Car
class must contain the following public member functions:- A default constructor,
Car()
, that initializes all the data members properly. - A function,
getUsage()
, that- Takes no arguments,
- Finds how many bins are in use, and
- Returns that information as a
size_t
.
- A function,
isEmpty()
, that- Takes no arguments,
- Finds whether the car is empty, and
- Returns that information as
bool
.
- A function,
isFull()
, that- Takes no arguments,
- Finds whether the car is full, and
- Returns that information as
bool
.
- A function,
addPackage()
,- That takes no arguments,
- Adds a package to the car, and
- Doesn't return anything.
- A function,
removePackage()
that- Takes no arguments,
- Removes a package from the car, and
- Doesn't return anything.
- A function,
printToStream(std::ostream& outStream)
, that- Takes an
std::ostream
,std::cout
is an example of anstd::ostream
, so someone might call this function asmyCar.printToStream(std::cout);
- Displays the car (by printing to the given
ostream
), and, - Doesn't return anything.
- Takes an
- A default constructor,
- Review the functions above and determine which ones don't modify the object they're called on.
- If a member function doesn't modify the object, declare it as
const
(by putting the keywordconst
on thefar right , after the close parenthesis of the parameter list. (This rule is pretty much universal.)
- If a member function doesn't modify the object, declare it as
Declare Global (Non-Class) Functions
Sometimes we need to write functions that aren't member functions for the class, but are closely associated with it. One such function is the <<
operator for printing, which is always a global function, but is often defined to work with a particular class.
-
Declare a global function (i.e., a function outside of the class) to allow a
Car
object to work with the streaming operator (<<
). Similar tooperator=
, the function is named:std::ostream& operator<<(std::ostream& os, const Car& c);
- This function should be declared outside of the class, after the closing curly brace,
}
, but before the the#endif
of your include guard.
- This function should be declared outside of the class, after the closing curly brace,
Remind me, what's the deal with
std::ostream
inprintTostream
?You've used a
std::ostream
before!std::cout
is anstd::ostream
that sends output text to the terminal!Another common example is a
std::ofstream
, which is anstd::ostream
that sends output text to a file.Why not just have a
print()
member function and always print tostd::cout
?What if we want to send the output somewhere else? Letting the user specify the stream they want the output sent to provides more flexibility.
MORE choices!
And what about that
operator<<
thing? What's that all about?When we say
cout << x;
, that is equivalent to writingoperator<<(cout, x);
. When we define our own version ofoperator<<
for a type (e.g., forCar
) allows us to specify what the<<
operator does to aCar
.In other words, then we can say:
std::cout << "This car is: " << myCar << std::endl;
Helpful Hints
You need to put std::
on the front of standard library types like std::ostream
.
But I can skip saying
std::
by saying:using namespace std;
That's okay to do if you want in an implementation file, but it's considered uncool in a header file, as means anyone who includes your header file gets the
std
namespace opened whether they wanted that or not.
(When logged in, completion status appears here.)