Implement Train
Member Functions
In this part of the assignment, you will implement the functionality of the Train
in the file train.cpp
. For each function, you'll fill out the stub function you wrote in Part 5.
You should compile and test your code frequently, and, of course, fix any problems you find along the way..
At the end of each step, we'll recommend various test functions in
testtrain.cpp
to exercise your new code.You should also run your code with Valgrind each round to check for memory leaks.
Let's get to it!
Implement Constructor, Destructor, printToStream()
, and operator<<
Train::Train(traintype type)
— The Parameterized Constructor
You should now write code to fill in the stub that you previously wrote. The Train
constructor should do the following:
- Initialize
cars_
to point to an array (allocated withnew
) with room for exactly one car. - Initialize
numCars_
to1
. - Initialize
usage_
to0
. - Initialize
revenue_
to0
. - Initialize
operatingCost_
to0
. - Initialize
type_
to the value of the input parametertype
(which will be eitherBASIC
orSMART
).
(All of the above can and should be done in the member-initialization list!)
Train::~Train()
— The Destructor
The destructor should free up all dynamically memory that was allocated by for this Train
.
Train::printToStream()
— The Output Function
This function displays specific information about a train.
It should:
- Output
(
- Output the revenue
- Output
,
and a single space - Output the operating cost
- Output
)
and a single space - Loop over all the cars
- Output each car
(Don't add any additional spaces or newlines! Don't print the revenue as dollars and cents, just as an number, which will be whole dollars given our constants.)
At this point you can only have trains with a single car, zero revenue, and zero operating cost, as we haven't added functions to change those values. A correctly printed train would look like
(0, 0) [_][_][_][_]~
Check Your Code So Far
- Confirm that your code still compiles without warnings using your
Makefile
. - Uncomment the first two
Train
test functions intesttrain.cpp
:testCreateTrain()
andtestOutputTrain_1()
. - Confirm that your code compiles with your
Makefile
and fix issues. - Run your
./testtrain
, both normally and with Valgrind and fix any issues.
Implement Functions for Adding Packages
Temporary Code for upsizeIfNeeded()
Change upsizeIfNeeded
to stop throwing an exception and just return without doing anything. If you like, you can make the function print a message to cerr
warning that it isn't actually doing its job.
Write addPackage()
Adding a package to the train will include
- If necessary, adding more cars to add another package.
- Loading the package.
- Updating
revenue
to reflect the charge to the customer.
Your function for adding a package to a train should do the following:
- Call
upsizeIfNeeded()
. Currently,upsizeIfNeeded()
is a stub that does nothing, but, for now, assume it works correctly and adds more train cars if there isn’t enough room to add another package. You will implementupsizeIfNeeded()
soon! - Call
loadPackage()
. As withupsizeIfNeeded()
, this function is still just a stub, so it won't do anything. Again, don’t worry about it yet! - We have done a lot of work, so let’s make some money now! Increase
revenue
bySHIPPING_COST
once we have accepted a package (and loaded it onto a train!), which is work for which we charge customers.
Write loadPackage()
The loadPackage()
function will take care of loading a package onto the appropriate car and then update usage_
and the operatingCost_
. Train cars are always filled from left to right. In general, a train might end up with some full cars towards the front, a car in the middle with room for more packages, and the rest of the cars empty.
For example,
(40, 12) [x][x][x][x]~[x][x][x][x]~[x][x][_][_]~[_][_][_][_]~
- We’ll leverage the
addPackage()
function we wrote earlier.- First, determine which car is both closest to the front of the train and has room for additional packages.
- Then, call that
Car
’saddPackage()
function. - Think about how would you use the
Train
data members to determine the index of the frontmost car with unused space.
- Increment the
usage_
variable. - As loading a package is some amount of work that Quinn’s company needs to perform, increment
operatingCost
byHANDLING_COST
.
Check Your Code So Far
Test that your function works by compiling and running it both normally and with Valgrind, using tests testTrainAddAndOutput_01()
, testTrainAddAndOutput_02()
, and testTrainAddAndOutput_03()
.
Implement Functions for Removing Packages
Write removePackage()
Your function for removing a package from a train will
- Remove a package from a train.
- Update
usage_
accordingly. - Decrease the number of train cars if necessary.
In particular, your function should
- Check whether the train contains a package to be removed. If so…
- Decrement the
usage_
variable. - Call the
Car
class’sremovePackage()
function on the rearmost car with any packages.- Think about how would you use the
Train
data members to figure out the index of that car.
- Think about how would you use the
- Call
downsizeIfNeeded()
. Currently,downsizeIfNeeded()
is a stub that does nothing, but assume it works correctly. We will implementdownsizeIfNeeded()
soon.
It is against the rules to call removePackage()
on an empty train. As with your code in the Car
class, you can choose how to handle people breaking the rules.
Check Your Code So Far
Test that your function works by compiling and running both normally and with Valgrind, using tests testTrainAddRemoveAndOutput_01()
, testTrainAddRemoveAndOutput_02()
, testTrainAddRemoveAndOutput_03()
.
Implement Functions for Changing the Size of the Train
Write upsizeIfNeeded()
At this point, we are going to take into account our two different train size-changing strategies. Our goal is to figure out if we need more space, and, if so, use the changeSize()
function to move all the packages to a larger train. We have not yet implemented the changeSize()
function, so it will actually do nothing at this point, but that's OK!
Your upsizeIfNeeded()
function should determine if the last car on the train is full, and, if so
- If the train is a
BASIC
train,- Call
changeSize()
with the current number of cars in the train plusBASIC_SIZE_CHANGE
, thus switching to a train withBASIC_SIZE_CHANGE
more cars than the train you're starting with.
- Call
- If the train is a
SMART
train,- Call
changeSize
with the current number of cars timesSMART_SIZE_CHANGE
, thus switching to a train that isSMART_SIZE_CHANGE
times the size of your original train. (We will investigate why this method is the “smart” thing to do after we get it all working.)
- Call
Write downsizeIfNeeded()
This function also takes our two different train size-changing strategies into account. If we call this function, we want to switch to a smaller train to avoid using too much unnecessary space (i.e., memory).
Your downsizeIfNeeded()
function should only downsize if there is more than one car in the train. To downsize,
- If the train is a
BASIC
train and the last car is empty, remove cars by callingchangeSize()
with the current number of cars minusBASIC_SIZE_CHANGE
. - If the train is a
SMART
train and the number of packages on the train (usage_
) is less than or equal to 1/SMART_DOWNSIZE_THRESHOLD
of the total current package capacity of the train, we callchangeSize()
with the current number of cars divided bySMART_SIZE_CHANGE
. For example, if we have a car using one-quarter of its total capacity, we want to switch to a new train that is half the size.
Write changeSize()
With changesize()
, we will finally be able to switch to larger or smaller trains. An important consequence of switching trains is that there is a handling cost for Quinn to transfer the packages from one train to another. To correctly capture this cost, you’ll need to use removePackage()
and loadPackage()
. Our outlined approach is not the only possible solution, but it is one that works. You can implement a different approach if you come up with one that makes sense to you!
Your changeSize(size_t size)
function should
-
Create a temporary pointer, say,
oldCars
, that points to the same place in the heap ascars_
. Also create a variable to store the number of old cars. -
We can’t resize our array, so we have to attach the engine to a fresh bunch of cars (i.e., create a new array).
- Set
cars_
to anew
memory location on the heap allocated to hold an array ofsize
cars. - Set
numCars_
to besize
. - Set
usage_
to0
.
- Set
-
Iterate over the old cars. For each old car,
- Simulate transferring the car's packages to the new train one at a time. In other words, you need to
- Remove each package from the old car (call that car’s
removePackage()
function). - Load each package onto the new
cars_
of the train by callingloadpackage()
.
- Remove each package from the old car (call that car’s
- Simulate transferring the car's packages to the new train one at a time. In other words, you need to
-
Clean up the old array pointed to by
oldCars
so no memory is leaked on the heap. (Metaphorically, return those cars to the station so they can be reused.)
Check Your Code So Far
Compile and use Valgrind with tests testBasicUpsize_01()
, testBasicUpsize_02()
, testSmartUpsize_01()
, testSmartUpsize_02()
, testBasicDownsize_01()
, testBasicDownsize_02()
, testSmartDownsize_01()
, testSmartDownsize_02()
.
(When logged in, completion status appears here.)