Week 9: Structs
CONTENTS:
- Readings
- Background
- PreQuiz (due Monday March 10 at 9am)
- Recitation
- Recitation Submission Guidelines
- Homework 7
Readings
Please note the advised readings of “Brief C++ Late Objects” - Cay Horstmann:
- Monday: 8.3, 7.7
- Wednesday: 7.7
- Friday: 7.7, 9.1, 9.2, 9.3
Background
File Output
We discussed reading from a file last week. Here, we will show how we can write to a file. First, you define an ofstream
variable and open the file you would like to write to. Then you can write to the output file using the same operation you used with cout
, <<
, and endl
.
// Create the output file object
ofstream file_out;
// Opening the output file
file_out.open("output.txt");
// Writing something to the output file
file_out << "writing a sentence to the output file" << endl;
// Writing the value of the variable to the file
string test_string = "you can also write the content of the variable";
file_out << test_string << endl;
file_out.close();
The output.txt will have the following content:
Structs
In C++, we can define a structure using the keyword struct like so:
struct State
{
string name;
int area;
}; // <-- semicolon after struct definition
This defines a new type, State, that you can use for declaring variables, e.g.
//create a State variable with no name or area
State emptyState;
//create a State variable with a name and area
State colorado{"Colorado", 104094};
The variables emptyState
and colorado
both have two named attributes, called members - name
and area
. We can access each member using dot notation, e.g.
//set members for empty State
emptyState.name = "Texas";
emptyState.area = 268596;
//get members for colorado
cout << colorado.name << " has an area of " << colorado.area << " square miles." << endl;
Expected output:
If we want to compare two structs, we cannot do so directly. Instead, we must compare each data member individually to see if they match, e.g.
//check each data member by one
if(colorado.name == emptyState.name && colorado.area == emptyState.area)
{
cout << "These are the same state!" << endl;
}
else
{
cout << "These are not the same state!" << endl;
}
Expected output:
Classes
When writing complex programs, sometimes the built-in data types (such as int, char, string) don’t offer developers enough functionality or flexibility to solve their problems. A solution is for the developer (you - yes, you!) to create your own custom data types to use, called classes. Classes are user-defined data types, which hold their own data members and member functions, which can be accessed and used by creating an instance of that class. A class is like a blueprint for an object, customized for whatever particular problem the programmer is working on.
String, for example, is a class in C++ which holds data (the characters comprising the string) and supports useful member functions like substr which operate on this data.
Below is an example of the basic definition of a class in C++.
class ClassName{
public:
//The public interface
//Member functions
private:
//The private data members
}; //must end with a semicolon
Anatomy of a Class
Class Name
A class is defined in C++ using the keyword class
followed by the name of the class. The body of the class is defined inside the curly brackets and terminated by a semicolon at the end. This class name will be how you reference any variables or objects you create of that type. For example:
ClassName objectName;
The above line would create a new object (variable) with name objectName
and of type ClassName
, and this object would have all the properties that you have defined within the class ClassName
.
Access Specifiers
Access specifiers in a class are used to set the accessibility of the class members. That is, they restrict access by outside functions to the class members.
Consider the following analogy:
Imagine an intelligence agency like the CIA, that has 10 senior members. Such an organization holds various sorts of information, and needs some way of determining who has access to any given piece of information. Some information concerning classified or dangerous operations may only be accessible to the 10 senior members of the agency, and not directly accessible by any other person. This data would be private.
In a class, like in our intelligence agency, these private data members are only accessible by a class’s member functions and not directly accessible by any object or function outside the class.
Some other information may be available to anyone who wants to know about it. This is public data. Even people outside the CIA can know about it, and the agency might release this information through press releases or other means. In terms of our class, this public data can be accessed by any member function of the class, as well as outside functions and objects, even other classes. The public members of a class can be accessed from anywhere in the program using the direct member access operator (.) with the object of that class.
Data Members and Member Functions
Data members are the data variables and member functions are the functions used to manipulate these variables; together, data members and member functions define the properties and behavior of the objects in a Class.
The data members declared in any class definition can be fundamental data types (like int
, char
, float
, etc.), arrays
, or even other classes.
For example, for string objects, the data member is a char array[]
that holds all of the individual letters of your string. Some of a string’s member functions that we have used already are functions like .substr()
and .length()
.
Accessing Data Members
Keeping with our intelligence agency example, the code below defines a class that holds information for any general agency. In the main function, we then create a new IntelligenceAgency
object called CIA
, and we set its organizationName
to “CIA” by using the access operator (.) and the corresponding data member’s name. However, we cannot access the classifiedIntelligence
data member in the same way. Not everyone has access to that private data.
Instead, we need to use a member function of the IntelligenceAgency
class, getClassifiedIntelligence()
, in order to see that information. This allows us to control the release of private information by our IntelligenceAgency
.
Additionally, the main function includes four different ways of creating objects with descriptions in the comments next to it.
class IntelligenceAgency
{
public:
IntelligenceAgency(); // Default constructor
IntelligenceAgency(string classified_intelligence_input); // Parameterized constructor
string organizationName;
string getClassifiedIntelligence();
void setClassifiedIntelligence(string classifiedIntelligenceInput);
private:
string classifiedIntelligence;
};
int main()
{
IntelligenceAgency CIA;
CIA.organization_name = "CIA";
cout << CIA.classifiedIntelligence; // gives an error
cout << CIA.getClassifiedIntelligence();
// four types of constructor calls
IntelligenceAgency abc; // creating an IntelligenceAgency object with the default constructor
IntelligenceAgency xyz = IntelligenceAgency(); // creating an IntelligenceAgency
//object with the default constructor
IntelligenceAgency pqr("PQR"); // creating an IntelligenceAgency
//object with a paramaterized constructor
IntelligenceAgency rst = IntelligenceAgency("RST"); // creating an IntelligenceAgency
//object with a paramaterized constructor
}
Defining Member Functions
You may have noticed that we declared various member functions in our class definition, but we did not specify how they will work when called. The body of the function still needs to be written. The solution is to define a function, such as getClassifiedIntelligence()
, corresponding to our class’s functions. But how does our program know that these functions are the same as the ones we declared in our class? You as the developer need to explicitly tell the computer that these functions you are defining are the same ones you declared earlier.
string IntelligenceAgency::getClassifiedIntelligence()
{
return classifiedIntelligence;
}
void IntelligenceAgency::setClassifiedIntelligence(string classifiedIntelligenceInput)
{
classifiedIntelligence = classifiedIntelligenceInput;
}
We start the definition as we do any function, with the return type. Then, we have to add a specifier IntelligenceAgency::
that lets our program know that this function and the one we declared inside the class are the same. We can see that this function returns the class’ classifiedIntelligence
to the user.
Functions like getClassifiedIntelligence()
are called accessor/getter functions. This is because they retrieve or `get’ the private data members of a class object and return it to the program so that these values may be used elsewhere.
The second function, setClassifiedIntelligence(string classifiedIntelligenceInput)
, is called a mutator/setter function. These allow functions from other parts of our program to modify or ‘set’ the private data members of our class objects. Getters and setters are the functions that our program uses to interact with the private data members of our class objects.
Structs and Classes
A class can have a struct as a data member, much like how a class could have any other type of data member. It’s important to make sure that the class header file (the .h file) can see the definition of the struct. This can be accomplished by defining the struct inside of the .h file, like below:
// filename: example.h
struct State
{
string name;
int area;
}; // don't forget the semicolon
class Example
{
private:
State my_state_;
//other data members
public:
//setter accepts a State parameter
void setState(State new_state);
//getter returns the State member variable
State getState();
//other member methods
};
Any file that includes the .h file shown above will be able to use instances of the State struct, so you will not need to define the State struct anywhere else.
When to use Structs vs Classes
While structs and classes are similar, there are some key differences between them. When deciding whether you should use one over the other, consider the functionality you’d like to achieve. A good rule of thumb is to ask whether the data type you’re defining will need to have any methods or private variables. If it will, then you should create a class. If all members can be public and there are no methods, then a struct may be better.
PreQuiz (due Monday March 10 at 9am)
Recitation
Recitation Spot The Error- Problem 1
The Louvre Museum wants to determine its busiest day of the week so it can allocate more staff to assist with guided tours. The museum keeps a log of daily visitors in a file called ‘visitors.txt’. Each line in the file has the following format:
dayOfWeek <space> visitor1,visitor2,..,visitorN.
Identify the error(s) in the code below and write the correct line(s).
#include <iostream>
#include <string>
using namespace std;
int main()
{
ifstream my_file("visitors.txt");
string full_line;
string days[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
int vis[5] = {0, 0, 0, 0, 0};
int i = 0;
int traffic = 0;
int j = 0;
while (getline(my_file, full_line))
{
for(int i = 0; i < static_cast<int>(full_line.length()); i+=1)
{
if(full_line[i] == ' ' && i < static_cast<int>(full_line.length())-1)
{
visitors[i]++;
}
if(full_line[i] == ",")
{
visitors[i]++;
}
}
if (visitors[i] < traffic)
{
traffic = visitors[i];
j = i;
}
i++;
}
cout << days[j] << " is the busiest day of the week at the mueseum." << endl;
return 0;
}
Recitation Valid Double - Problem 2
Design a function validateDouble
that accepts a string input and determines if it represents a valid double by iterating through the string. Valid doubles can start with a minus sign and can have up to one decimal point. Your program should ask the user to input a double, store it as a string and then invoke validateDouble
function to check its validity. The program should then print whether the string is a valid double or not. (Negative double are also valid doubles. You can reuse some parts of your validateInt
function from week 5 homework.
Function: | validateDouble(string) bool validateDouble(string input) |
Purpose: | Iterate through a string and verify if it is a valid double or not. The function should not print anything. |
Parameters: | string input - The string to be verified |
Return value: | It returns true if the string is a valid double. Otherwise, it returns false . |
Error handling && Boundary conditions: | If length of input = 0, false is returned |
Expected input/output pairs:
Recitation Midterm Averages- Problem 3
The file ‘midterms.txt’ contains a list of comma-separated scores for three midterms taken by each student in a class. Each line represents one student’s scores for all three midterms. Your task is to compute the average score for each midterm across all students.
To complete this task, you must:
- Read each line in as a string,
- Use your
split()
function from previous homework to separate each line at the commas, - Use the
validateDouble()
function from problem 2 to confirm that the pieces of each line translate into doubles, and then you can usestod
to translate those valid strings to doubles.
Examples runs:
If the file contains the following lines:
Expected output:
If the file contains the following lines:
Expected output:
If the file contains the following lines:
Expected output:
Recitation Midterm Averages- Problem 3.a. : Algorithm
Write out the steps you would use to solve this problem by hand as pseudocode.
Recitation Midterm Averages- Problem 3.b. : Implementation
Translate your pseudocode into a C++ program to solve the above code, verify that your program works as expected.
Recitation Submission Guidelines
Important: Follow these instructions carefully when preparing your recitation assignments. Your final submission should be in a single document, and the only action required on Canvas is uploading that document.
- Documentation:
- Create a pdf that includes your submission for all recitation questions. This is the pdf you will upload to your canvas assignment. Feel free to use Word/Google doc to create the pdf.
- Clearly label each question with its corresponding number and include content as applicable (see #2).
- Content to Include:
- Screenshots of Your Code:
- For each question, include a screenshot of your code.(corrected code in case of spot the errors)
- Screenshots of Code Output (if applicable):
- For some longer questions, it might be required to take a screenshot of the code’s output. Include these screenshots as part of your submission.
- Longer Recitation Questions (Multiple Parts):
- Option A:
- Comment your answers directly within your code file.(Spot the errors)
- Take screenshots of the commented code and paste them into your document.
- Option B:
- Take screenshots of the unmodified code.
- Write your answers (Free Response/Pseudocode/Edge case identifictation) to the subquestions in the pdf document next to the corresponding screenshots.
- Option A:
- Screenshots of Your Code:
- Submission:
- Upload the final pdf document to Canvas. This is the only action required on Canvas for your submission.
By following these steps, your submission will be clear, organized, and standardized across all recitation assignments.
Homework 7
Warning: You are not allowed to use global variables for this assignment.
All function names, return types, and parameters must precisely match those shown. You may not use pass by reference or otherwise modify the function prototypes. You are welcome to create additional functions that may help streamline your code.
All files used in this assignment are in a zipped folder titled ‘homework_7_input_files.zip’ also located in Canvas under the week 9 module.
QUESTION 1: National Park
This question may require the use of file streams, loops, and arrays.
Write a C++ program to read a file of national park names and print them to the console in the same order as they appear in the file.
Note: There are 63 national parks in the USA, which is why our MAX_SIZE is set to 63.
For this question, the answer box on the coderunner is preloaded with the following solution template. You are only required to fill in the appropriate blanks. Additionally, you may use the national_parks_1.txt file to test out your code. All the files used in this assignment, including national_parks_1.txt, are available in Canvas under week 10 module. You may also create more files for further testing.
#include <iostream>
#include <fstream>
using namespace std;
void listNationalParks(string filename)
{
// initialize variables
ifstream file_in(filename); // opens a file
if (file_in.fail())
{
cout << "This file does not exist." << endl; // or something else went wrong opening the file
return;
}
const int MAX_SIZE = 63;
string parks[MAX_SIZE];
string park;
int idx = 0;
// Fill in the blank below with code to read from the file into the 'park' variable
_______________________________
{
parks[idx] = park;
idx += 1;
// Fill in the blank below with code to check if you are trying to store more values
// than possible in the parks array
__________________________
{
break;
}
}
for (int i = 0; i < idx; ++i)
{
cout << i << ": " << parks[i] << endl;
}
file_in.close();
}
int main()
{
string filename;
cout << "Enter national parks filename" << endl;
cin >> filename;
listNationalParks(filename);
return 0;
}
For Question 1, develop and validate your solution in VS Code. Once you are happy with your solution, go to coderunner on Canvas and replace the blank with your solution. The answer box is preloaded with the template above.
QUESTION 2: Museum Attendance
This question may require the use of file streams, loops, and arrays.
Your former high school took students on a museum trip. Before boarding the bus, the teachers made a list of all the students’ names. After visiting the museum, the teachers asked the students to board the bus and made a new list of all the students’ names. Before leaving, the teachers wanted to compare the two lists to make sure no student was left behind. Write a C++ program to help them do this quickly and easily. Your program compares the two attendance sheets and reports if any student is still inside the museum.
Note: The largest file has 24 names; therefore, declare your array with size 30 to ensure it accommodates all test cases.
void compareAttendanceSheet( string first_attendance_file,
string second_attendance_file )
{
// ...
}
Function: | compareAttendanceSheet( string, string ) |
Purpose: | Given two attendance sheets, the function will compare and print out any individuals who are present in the first sheet but missing in the second. |
Parameters: | string first_attendance_file - The first attendance sheet. string second_attendance_file - The second attendance sheet. |
Return Value: | N/A. |
Error Handling: | - If either file does not exist, print “Failed to open attendance files”. - If a name exists in the second_attendance_file , it will also be present in the first_attendance_file . In other words, no new names are added to the second_attendance_file that were not in the first. - Hint: The largest file has 24 names; therefore, declare your array with size 30 to ensure it accommodates all test cases. |
Example:
// This is only an example usage, and you should develop your own main function
// Assume the proper libraries are included.
// Assume the proper implementation of compareAttendanceSheet() is included.
int main() {
compareAttendanceSheet("example_1.txt", "example_2.txt");
return 0;
}
Sample Input:
Sample from example_1.txt:
Sample from example_2.txt:
Sample Output:
For Question 2, develop and validate your solution in VS Code. Once you are happy with your solution, go to coderunner on Canvas and paste compareAttendanceSheet() and any helper function(s) to the answer box!
QUESTION 3: Compare Trails
This question may require the use of file streams, stringstream/split(), loops, and functions
Today is your first day as a software engineering intern with the National Park Service. You’ve started on a busy day, and after the initial meetings, your boss asked you to develop a function to print some statistics about some of the long hikes around the world. You’ve been given a text file, which contains a list of different hikes. Each line includes the name of a long hike, its length (as a whole number) in miles, and its elevation gain (as a whole number) in feet, each separated by the delimiter |
.
Hint: Your split( )
function from homework 6 could be helpful. Alternatively you can use stringstream
directly.
Function: | printHikeStats(string) void printHikeStats(string file_name) |
Purpose: | Given a text file that contains information on the trails, the function will print the following: - Number of valid lines read from the file. A valid line must contain exactly three fields: the hike name, length (as an integer), and elevation gain (as an integer). - Name and the length of the longest hike. - Name and the length of the shortest hike. - Name and elevation gain per mile of the steepest hike. This number should be rounded to one decimal point (use setprecision). |
Parameters: | string file_name - The file name of the text file. |
Return Value: | N/A. |
Error Handling: | - If the file does not exist, print ``Could not open file.” - If there is a tie, choose the first one. |
Example:
// This is only an example usage, and you should develop your own main function
// Assume the proper libraries are included.
// Assume the proper implementation of printHikeStats() is included.
int main() {
printHikeStats("long_hikes.txt");
return 0;
}
For Question 3, develop and validate your solution in VS Code. Once you are happy with your solution, go to coderunner on Canvas and paste printHikeStats() and any helper function(s) to the answer box!
QUESTION 4: Restaurant Struct
This question may require the use of structs and functions.
You are tasked with designing a program to evaluate restaurants based on a food quality rating, cleanliness rating, and wait time rating, ultimately providing an overall rating. Write a C++ program to accept the name of a restaurant, its food quality rating (0-10), cleanliness rating (0-10), and wait time rating (0-5). Use a function to calculate the overall rating using the function specification and formula given below:
// Overall Rating = 0.5 * food_quality
// + 0.3 * cleanliness
// + 0.2 * wait_time
You must create a struct named Restaurant with the following attributes: the name of the restaurant, food quality rating, cleanliness rating, wait time rating, and overall rating.
Member Type | Member Name | Description |
string |
name | Restaurant’s name |
int |
food_quality | Restaurant’s food quality rating (0-10) |
int |
cleanliness | Restaurant’s cleanliness rating (0-10) |
int |
wait_time | Restaurant’s wait time rating (0-5) |
double |
overall | Restaurant’s overall rating |
Next, create a function that will calculate the overall score of each restaurant.
Function: | getOverallRating(Restaurant) double getOverallRating(Restaurant restaurant) |
Purpose: | Given a object of type Restaurant, calculate its overall rating. |
Parameters: | Restaurant restaurant - A Restaurant object that contains all the information of the object. |
Return Value: | The overall rating of the restaurant. The function should not print anything. |
Error Handling: | - If any of the ratings in restaurant are not within its bound, return -1. The rating bounds are: food quality (0-10), cleanliness (0-10), wait time (0-5) |
Example:
// This is only an example usage, and you should develop your own main function
// Assume the proper libraries are included.
// Assume the proper implementation of getOverallRating() and Restaurant struct is included.
int main() {
Restaurant r;
r.name = "McDonalds";
r.food_quality = 4;
r.cleanliness = 7;
r.wait_time = 5;
r.overall = getOverallRating(r);
if(r.overall == -1)
{
cout << "Invalid rating(s) entered." << endl;
}
else
{
cout << "Restaurant: " << r.name << " Overall: " << r.overall << endl;
}
return 0;
}
Sample Output:
For Question 4, develop and validate your solution in VS Code. Once you are happy with your solution, go to coderunner on Canvas and paste Restaurant struct, getOverallRating(), and any helper function(s) to the answer box!
The sample inputs will be given in the order of name, food quality, cleanliness, and wait time, respectively.
The sample inputs will be given in the order of name, food quality, cleanliness, and wait time, respectively.
QUESTION 5: List of Restaurants
This question may require the use of file streams, split(), loops, arrays, and structs.
In this question, we will use the same struct for Restaurant from Question 4. You will be given a file, which contains the name of the restaurant, followed by its food quality, cleanliness rating, and wait time rating, all separated by ~
The task will be to evaluate the best restaurant from within the list.
Part A:
First, write a function that reads the restaurant details from a file and populates the required array for further analysis.
Hint: The split( )
function from homework 6 could be helpful.
int readRestaurantDetails( string filename,
Restaurant restaurant[],
const int MAX_RESTAURANTS )
{
// ...
}
Function: | readRestaurantDetails( string, Restaurant[], const int ) |
Purpose: | Read all the restaurant details from a given file and calculate the overall rating for all restaurants. |
Parameters: | string filename - The text file that contains all the details of the restaurants. Restaurant[] restaurants - The array that will be populated with all the restaurant objects. const int MAX_RESTAURANTS - The maximum number of restaurants that can be present in the file containing restaurant details. |
Return Value: | If successful (function parameters are correct), return the number of restaurants added to the array. The function should not print anything. |
Error Handling: | - If the file does not exist, -1 is returned. - If there is an incorrect number of attributes for a given line of the file, skip the line. |
Example:
// This is only an example usage, and you should develop your own main function
// Assume the proper libraries are included.
// Assume the proper implementation of readRestaurantDetails(), getOverallRating(), and Restaurant struct is included.
int main()
{
Restaurant restaurants[30];
int res_size = readRestaurantDetails("restaurants.txt", restaurants, 30);
// Checking if the file was opened correctly
if (res_size == -1)
{
cout << "Failed to open file." << endl;
}
else
{
for (int i = 0; i < res_size; ++i) {
cout << "Restaurant: " << restaurants[i].name << " ";
cout << "Ratings: ";
cout << restaurants[i].food_quality << " ";
cout << restaurants[i].cleanliness<< " ";
cout << restaurants[i].wait_time << " ";
cout << "Overall: ";
cout << restaurants[i].overall << " ";
cout << endl;
}
}
}
Sample Output:
Part B:
Then, create a feature where we can find the best restaurant according to your criteria. The possible options are:
- Option 1: "Food Quality"
- Option 2: "Cleanliness"
- Option 3: "Wait Time"
- Option 4: "Overall"
int getBest( Restaurant restaurants[],
int arr_size,
string metric )
{
// ...
}
Function: | getBest(Restaurant[], int, string) |
Purpose: | Given an array, the function will find and return the index of the best restaurant according to the metric. |
Parameters: | Restaurant[] restaurants - The array that has been populated with the details of all the restaurants. int arr_size - The maximum number of restaurants that are present in the restaurants array. string metric - The metric used to find the best restaurant. |
Return Value: | If successful (function parameters are correct), return the index of the best restaurant according to the metric. The function should not print anything. |
Error Handling: | - If the metric is not a valid option (not “Food Quality,” “Cleanliness,” “Wait Time,” or “Overall”), -1 is returned. - If there is a tie, choose the first one. - Note: in this assignment, a higher wait time rating means a shorter wait, which indicates better service. For example, a wait time rating of 5 indicates excellent service with minimal wait. |
Example:
// This is only an example usage, and you should develop your own main function
// Assume the proper libraries are included.
// Assume the proper implementation of getBest(), readRestaurantDetails(), getOverallRating(), and Restaurant struct is included.
int main()
{
// PART A
Restaurant restaurants[30];
int res_size = readRestaurantDetails("restaurants.txt", restaurants, 30);
// Checking if the file was opened correctly
if (res_size == -1)
{
cout << "Failed to open file." << endl;
}
else
{
for (int i = 0; i < res_size; ++i) {
cout << "Restaurant: " << restaurants[i].name << " ";
cout << "Ratings: ";
cout << restaurants[i].food_quality << " ";
cout << restaurants[i].cleanliness<< " ";
cout << restaurants[i].wait_time << " ";
cout << "Overall: ";
cout << restaurants[i].overall << " ";
cout << endl;
}
}
// PART B
// Checking if the file was opened correctly
if (res_size == -1)
{
cout << "Failed to open file." << endl;
}
else if (res_size == 0)
{
cout << "Empty file." << endl;
}
else
{
int idx = getBest(restaurants, res_size, "Food Quality");
if (idx == -1)
{
cout << "Invalid metric." << endl;
}
else
{
cout << "Restaurant: " << restaurants[idx].name << " ";
cout << "Ratings: ";
cout << restaurants[idx].food_quality << " ";
cout << restaurants[idx].cleanliness << " ";
cout << restaurants[idx].wait_time << " ";
cout << "Overall: ";
cout << restaurants[idx].overall << " ";
cout << endl;
}
}
}
Sample Output:
For Question 5, develop and validate your solution in VS Code. Once you are happy with your solution, go to coderunner on Canvas and paste the Restaurant struct, getOverallRating(), readRestaurantDetails(), getBest(), and any helper function(s) to the answer box!