Lab 04: struct pointers

Work to complete these exercises in lab today. Whatever you do not finish in the lab, try to complete by the start of lecture on Wednesday.


This repository contains a few sample C++ files that you will modify for this lab assignment. The work will give you more practice with arrays and structs. In particular, we’re allocating these now on the heap, and we access them through pointers.

Syntax summary

In the last week we’ve learned how to allocate arrays and structs in heap memory. We use new to allocate in this way, and we get back a pointer to either a single item of that type, or to an array of that type. For example, the lines:

int* a = new int[10];
int* p = new int;

create two pointer variables. The first allocates space for an array of 10 integers on the heap, 40 bytes. The second allocates space for a single integer in the heap (4 bytes of space). The contents of that memory can be accessed using the * operator, like so:

(*p) = 37;
(*p) = (*p) + 100;

We can allocate structs on the heap. For example, if we take the car struct defined in the last homework:

struct car {
  std::string make;
  std::string model;
  double odometer;
  double fuel;
  double mpg;
};

you can allocate an initialize a car on the heap with the lines:

car* aCar = new car;
(*aCar).make = "VW";
(*aCar).model = "bus";
(*aCar).odometer = 12338.0;
(*aCar).fuel = 10.8;
(*aCar).mpg = 19.09;

Notice the type car* of the variable aCar, a pointer to a car struct. Notice also the use of the (*aCar) notation on the subsequent lines of code. These each give you access to the contents of the struct, using that aCar pointer. The term (*aCar).mpg allows you to access that heap struct’s mpg field.

In C++ 2011 (often called “C++11”) and beyond (you’ll probably want to start using C++17) you can use initializer lists to set the fields of this new struct, like so:

car* aCar = new car {"VW","bus",12338,10.8,19.09};

Finally, when we are done with a heap-allocated struct, we use delete to release its storage back to the heap, like so:

delete aCar;

Notice that, since we allocated a single struct, not an array, we don’t include the brackets [] after the delete keyword.


A Fleet Database


Each of the following exercises below has you extend or modify the code in the file fleet.cc. This code has a main that is the “driver” procedure for managing a fleet of cars, stored in a “fleet database” file. When run, the code reads in a text file describing a collection of cars, and then loops asking its user to examine or modify the cars in that fleet. When done, it saves the modified vehicle’s status back into that text file.

The fleet database file has a simple format. You can examine and use the sample one called fleet.txt. The file starts with a line with the number of cars in that fleet, and then is followed by a series of database entries, each a series of lines describing the car. Here below is an example one-car fleet:

1

VW bus
112338 25.5
2.50943 10.8
19.09
100000

The top entry line contains the vehicle’s make and model. The next line contains the miles its been driven and the reading of its trip odometer. The third line gives the fuel in its tank followed by the fuel capacity. The fourth gives the gas mileage in MPG. The last gives the mileage of its last tune-up.

These get read by the program and several car structs are created, as an array. We’ve enhanced the struct definition slightly to contain these three additional fields:

struct car {
  std::string make;
  std::string model;
  double last;       // odometer reading of the last tune-up
  double odometer;
  double trip;       // miles since the last refuel stop
  double fuel;
  double mpg;
  double tank;       // capacity of the fuel tank
};

Once this file is read, the program loops requesting any of the following commands:

list
drive <make> <model>
refuel <make> <model>
tune <make> <model>
exit

The commands “list” and “drive” are partially implemented. You’ll complete their code for this lab. You’ll also get “refuel” and “tune” working. These all rely on the function lookup which is currently broken. So, as it stands, you’ll only be able to modify the first vehicle in the database. (Using fleet.txt, this is the “VW bus”.)

Your task is to fix this code, as outlined in the exercises below, and in the comments for each function that you need to fix.


Exercises


Exercise 1. tuneUp

A car now tracks when it last had a tune-up. This is stored as a number of miles in the field last. It gives the reading of the odometer when the last tune-up occurred. If the odometer reads 112338.0 and the last field is 1000000.0, this means that the car has driven 12338 miles since its last tune-up.

You like to keep a well-maintained fleet, so you work to tune-up a car every 10000 miles.

Write the code for the function tuneUp so that it modifies the car struct it is given according to the function’s comment. Note that the code should return a boolean value indicating whether a tune-up was performed.


Exercise 2. MPG

It turns out that cars get worse gas mileage when they need a tune-up. When they need a tune-up before a drive, then on that drive the car is less fuel efficient. It can travel 2.0 fewer miles for each gallon of fuel than its manufacturer rating (recorded in its mpg field). So our “VW bus” would get only 17.08 MPG in its current state, having needed a tune-up for the last 2338 miles.

Part 1. Write the code for the function MPG that gives the actual fuel efficiency of a car, based on its mpg rating and its last tune-up.

Our code, by the way, assumes that all cars are rated to get more than 2.0 miles per gallon.

The procedure drive now modifies a car struct by being handed a pointer to that struct. So it takes a paramter of type car*.

Part 2. Update the drive code so that it calculates the proper fuel efiiciency of the car to determine the fuel needed for that drive. It should call the MPG function rather than using the car’s mpg field directly. It should also update the trip odometer field, in addition to the manufacturer’s odometer field.


Exercise 3. refuel

Write the code for the function refuel which adds fuel to a car’s fuel tank whenever that level is below the tank’s capacity. It should return the amount (in gallons) of fuel that was added. It should reset the car’s trip odometer back to 0.0.


Exercise 4. outputCar

The code for outputCar gives the status of the given vehicle by writing that info out to std::cout. Add code to it so that, if it needs a tune-up, it then outputs a line:

The car is due for a tune-up.

Finally, it should output the car’s trip status, with a line like:

It has been driven 25.5 miles since the last time we refueled,
and can driven about another 42.8610 miles with the fuel it has.

Use the MPG function to calculate that range estimate.


Exercise 5. lookup

As the code stands, it uses the lookup function to scan the fleet of cars, looking for the car struct of the requested make/model. The code for lookup is broken. It always returns a pointer to the first vehicle in the fleet, the one at index 0 in the array.

Fix the code so that it returns a pointer to the car that matches the given make and model string. If no such car exists in the fleet, return the value nullptr.

We’ll talk about nullptr in class, For now, you can think of it as being used similarly here to Python’s None value. It is the “invalid entry” response to the lookup query.


Exercise 6 delete

Finally, note that our code uses new in the function readFleet to allocate heap space for the fleet struct and for its array of car structs that hold the fleet’s info. This means that their data needs to be released back to the heap.

Add the appropriate lines at the end of main so that these two pointers’ data is given back to the heap.