rename lab 3
This commit is contained in:
@@ -1,281 +0,0 @@
|
|||||||
# Overview
|
|
||||||
|
|
||||||
In this lab, you will be writing your own C++ classes. <!--We have not covered C++ classes in the lecture, but according to last Friday's poll, everyone is this class has either experience in Java or experience in Python, so the concept of class is not new to you.--> To get familiar with the C++ syntax on classes, you are recommended to quickly review the following 3 files:
|
|
||||||
|
|
||||||
[date.h](../../lectures/03_classes_I/date.h)
|
|
||||||
[date.cpp](../../lectures/03_classes_I/date.cpp)
|
|
||||||
[date_main.cpp](../../lectures/03_classes_I/date_main.cpp)
|
|
||||||
|
|
||||||
Some notes about these 3 files:
|
|
||||||
|
|
||||||
- date.h and date.cpp are the implementation of a class called *date*.
|
|
||||||
- date_main.cpp is the program to test the *date* class, and therefore, only this file contains the *main* function.
|
|
||||||
- the file with the .h file name extension is called the header file, and the .cpp file is callled the implementation file. Note that in C++ the name of the header and implementation files are not required to exactly match the name of the class, but it is good coding style to do so.
|
|
||||||
<!--- in this lab, you're not required to use the keyword **const**, as we will cover more about it in lecture. For the same reason, for now, you can also ignore the keyword **const** that is used in the *date* class.-->
|
|
||||||
- typically in C++, we declare a class in the header file, and then define/implement this class in a .cpp file. The declaration of a class includes the prototype of its member functions, and it also includes the member variables of this class. As can be seen from *date.h* and *date.cpp*, the *date* class has 3 member variables:
|
|
||||||
```cpp
|
|
||||||
int day;
|
|
||||||
int month;
|
|
||||||
int year;
|
|
||||||
```
|
|
||||||
and it has several member functions. Take the *isLeapYear* function for example, we write its prototype in the header file as below:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
bool isLeapYear() const;
|
|
||||||
```
|
|
||||||
|
|
||||||
and we define it in the .cpp file as following:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
bool Date::isLeapYear() const {
|
|
||||||
return (year%4 ==0 && year % 100 != 0) || year%400 == 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
pay attention to the "Date::" right in front of the function name *isLeapYear*. The "Date::" defines the scope of this function, meaning that this function is a member function of the *Date* class.
|
|
||||||
- typically in C++, we declare member variables as *private*, and declare member functions as "public". Member functions allows user to operate on these member variables, which can not be accessed directly from outside the class, and that is the meaning of *private*.
|
|
||||||
- constructor: In C++ classes, there is a special member function called the *constructor*. When an object of the class is created, the constructor will be automatically called. In the constructor function, we usually initialize the member variables of the object (that is being created). Constructors in C++ have the following characteristics:
|
|
||||||
- Constructors have the same name as the class they belong to.
|
|
||||||
- They do not have a return type, not even *void*.
|
|
||||||
- A class can have multiple constructors, and this is known as constructor overloading. Take the *Date* class as an example, it has the following two constructors:
|
|
||||||
```cpp
|
|
||||||
Date();
|
|
||||||
Date(int aMonth, int aDay, int aYear);
|
|
||||||
```
|
|
||||||
|
|
||||||
and their definitions are:
|
|
||||||
```cpp
|
|
||||||
Date::Date() { //default constructor
|
|
||||||
day = 1;
|
|
||||||
month = 1;
|
|
||||||
year = 1900;
|
|
||||||
}
|
|
||||||
|
|
||||||
Date::Date(int aMonth, int aDay, int aYear) { // construct from month, day, & year
|
|
||||||
month = aMonth;
|
|
||||||
day = aDay;
|
|
||||||
year = aYear;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
typically, the constructor which does not take any arguments, is called the **default constructor**. When a user creates a Date object like this:
|
|
||||||
```cpp
|
|
||||||
Date a;
|
|
||||||
```
|
|
||||||
|
|
||||||
the default constructor will be called; but if a user creates a Date object like this:
|
|
||||||
```cpp
|
|
||||||
Date Sallys_Birthday(9,29,1995);
|
|
||||||
```
|
|
||||||
|
|
||||||
then the second constructor will be called. Therefore you can see, when multiple constructors are defined, which constructor will be called depends on how the object is created - if no arguments are provided, then the default constructor will be called.
|
|
||||||
|
|
||||||
## Checkpoint 1
|
|
||||||
*estimate: 15-25 minutes*
|
|
||||||
|
|
||||||
For the first checkpoint, you will be working in groups of 4 to design the Animal.h file for a new class.
|
|
||||||
Keep in mind that you should only put the class defintion in the header file, you are not writing the actual
|
|
||||||
implementation. Make sure that everyone in the group participates and understands the material - it is
|
|
||||||
possible that all of you will have to answer questions to get checked off!
|
|
||||||
|
|
||||||
Each instance of the Animal class will describe several characteristics about an animal. Every Animal must
|
|
||||||
have a name at all times. This is the only value that will be passed in when a new object is declared. It
|
|
||||||
should be possible to set the weight of the animal, if the animal can survive on land, if the animal can survive
|
|
||||||
in water, if the animal eats meat, and if the animal eats plants. All of these values (including the name)
|
|
||||||
should be accessible from outside the class as well. In addition, the class should be able to indicate if the
|
|
||||||
animal in question is an omnivore (eats both meat and plants) and if they are amphibious (can survive on
|
|
||||||
land and in water).
|
|
||||||
|
|
||||||
Make sure to explain the purpose of every function with a comment. Consider the return type, whether each
|
|
||||||
argument should be passed by value, reference. Be prepared to justify your choices. To get
|
|
||||||
checked off, show your completed header file and answer any questions asked by the TA/mentor.
|
|
||||||
|
|
||||||
<!-- or constant reference, and whether or not the function is a
|
|
||||||
constant member function (has const after the argument list). -->
|
|
||||||
<!-- we should not ask anything about the keyword constant, as that hasn't been thoroughly covered in class. -->
|
|
||||||
|
|
||||||
**Note:** Only Checkpoint 1 is a team exercise. For the rest of this lab, you will implement a simple
|
|
||||||
C++ class named Time. It represents all possible times in a 24-hour period, including hours, minutes and
|
|
||||||
seconds. An immediate representation issue is how to handle morning (am) and afternoon (pm) times. We
|
|
||||||
could have a separate bool indicating whether the time is am or pm. It is easier, however, to represent the
|
|
||||||
hours in military time. This means that the hours of the day are numbered from 0 to 23, with 13 being 1 pm,
|
|
||||||
14 being 2 pm, etc.
|
|
||||||
|
|
||||||
## Checkpoint 2:
|
|
||||||
*estimate: 30 minutes*
|
|
||||||
|
|
||||||
In the second checkpoint you will get started by implementing the initial class design, several member
|
|
||||||
functions, and a simple main program to test your class.
|
|
||||||
|
|
||||||
The instructions below describe how to build your executable from the command line using g++ or
|
|
||||||
clang++ using the WSL or UNIX terminal. Even if you plan to use Visual Studio or another IDE for the
|
|
||||||
bulk of your work this semester, you are required to also show that you can successfully build and run this
|
|
||||||
lab using g++ from a terminal on your own machine.
|
|
||||||
|
|
||||||
- We provide basic testing code in [main.cpp](./main.cpp). You’ll need to create 2 new empty code files named time.h and time.cpp.
|
|
||||||
|
|
||||||
- Begin work on time.h. Within the file, declare a class called Time. Follow the form and syntax of the Date class. Read the syntax carefully (such as the semi-colon at the end of the class declaration). Add private member variables for the hour, minute and second. In the public area of the class, declare two constructors: one, the default constructor, should initialize each of the member variables to 0; the other, having three arguments, accepts initial values for the hour, minute and second as function call arguments. Declare member functions to access the values of the hour, the minute and the second (three different member functions). Don’t write the body of any of the functions in the time.h file. Save all the implementation for the time.cpp file.
|
|
||||||
<!-- It will be crucial for Checkpoint 3 to make these const. (Recall: a const member function can not change the member variables.) -->
|
|
||||||
|
|
||||||
- Review the provided main.cpp. Note that we must #include "time.h" in addition to including #include <iostream>. (Note: We use angle brackets for standard library includes and double quotes for our custom header files in the working directory.) The main program creates multiple Time objects, using the two different constructors and uses the functions that access the values of hour, minute and second by printing the two times.
|
|
||||||
|
|
||||||
Note: There is a common confusion when creating a new variable using the default constructor:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
Time t1(5,30,59); // calls the non-default constructor w/ 3 integer arguments
|
|
||||||
Time t2(); // COMPILE ERROR - a buggy attempt to call the default constuctor
|
|
||||||
Time t3; // the *correct* way to call the default constructor
|
|
||||||
```
|
|
||||||
|
|
||||||
Now implement all of the class constructors and member functions in the file time.cpp. Don’t forget to
|
|
||||||
add the line to #include "time.h". Any file that uses or implements Time functionality must include
|
|
||||||
the Time class header file.
|
|
||||||
|
|
||||||
Now, compile your program and remove errors. Here’s where the difference between compiling and
|
|
||||||
linking matters.
|
|
||||||
|
|
||||||
When compiling using g++ on the command line, the two separate command lines:
|
|
||||||
|
|
||||||
```console
|
|
||||||
g++ -c main.cpp -Wall -Wextra
|
|
||||||
g++ -c time.cpp -Wall -Wextra
|
|
||||||
```
|
|
||||||
|
|
||||||
compile the source code to create two object code files called main.o and time.o separately. The -c
|
|
||||||
means “compile only”. Compiler errors will appear at this point. If there are errors in main.cpp (or
|
|
||||||
time.cpp), then the files main.o (or time.o) will not be created. Use the *ls* command to check.
|
|
||||||
|
|
||||||
**Important Note**: We only compile .cpp files. We do not directly compile header files. Header files are
|
|
||||||
compiled only indirectly when included in a .cpp file.
|
|
||||||
|
|
||||||
Once you have driven out all of the compiler errors, you can “link” the program using the command:
|
|
||||||
|
|
||||||
```console
|
|
||||||
g++ main.o time.o -o time_test.exe
|
|
||||||
```
|
|
||||||
|
|
||||||
to create the executable called time_test.exe. If you have not defined all of the necessary member
|
|
||||||
functions in the Time class, then you would see “linking” errors at this point. You can combine all
|
|
||||||
three command lines (compiling each of the 2 .cpp files to 2 object files and linking all object files) with this command:
|
|
||||||
|
|
||||||
```console
|
|
||||||
g++ main.cpp time.cpp -o time_test.exe -Wall -Wextra
|
|
||||||
```
|
|
||||||
|
|
||||||
Which is more similar to what we did last lab. Equivalently, if those are the only two .cpp files in the
|
|
||||||
current directory, you can compile and link using the command line wildcard:
|
|
||||||
|
|
||||||
```console
|
|
||||||
g++ *.cpp -o time_test.exe -Wall -Wextra
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that this will not create the intermediate .o files and will only proceed to the linking step if the two files compile cleanly.
|
|
||||||
|
|
||||||
**To complete this checkpoint**: Show compilation of the program using g++/clang++ within the
|
|
||||||
WSL or UNIX terminal, with all compiler errors removed and demonstrate correct execution of your
|
|
||||||
program. Yes, please show us you can compile from the terminal with g++, even if you plan to primarily
|
|
||||||
use Visual Studio or another IDE for the rest of the semester.
|
|
||||||
|
|
||||||
## Checkpoint 3
|
|
||||||
*estimate: 20-30 minutes*
|
|
||||||
|
|
||||||
Create and test a few more member functions. This will require modifications to all three of the files. You should uncomment the provided tests in main.cpp as you work, and add your own tests.
|
|
||||||
- *setHour, setMinute, setSecond*. Each should take a single integer argument and change the appropriate
|
|
||||||
member variable. For now, do not worry about illegal values of these variables (such as setting the
|
|
||||||
hour to 25 or the minute to -15). Assume whoever calls the functions does the right thing. In general,
|
|
||||||
this is a bad assumption, but we will not worry about it here.
|
|
||||||
- *PrintAmPm* prints time in terms of am or pm, so that 13:24:39 would be output as 1:24:39 pm. This
|
|
||||||
member function should have no arguments. Note that this requires some care so that 5 minutes and
|
|
||||||
4 seconds after 2 in the afternoon is output as 2:05:04 pm. The output should be to std::cout.
|
|
||||||
|
|
||||||
<!--- Finally, let’s create a vector of times, sort it, and output the final order. You’ll need to create a
|
|
||||||
non-member function called IsEarlierThan which has the prototype:
|
|
||||||
bool IsEarlierThan(const Time& t1, const Time& t2);
|
|
||||||
It is very important that the two time objects are passed by constant reference. The prototype should
|
|
||||||
be in time.h (in the file, but outside of the class declaration) and the implementation should be in
|
|
||||||
time.cpp. It should return true if t1 is earlier in the day than t2. The tough part, from the logic
|
|
||||||
viewpoint, is being able to compare two times that have the same hour or even the same hour and the
|
|
||||||
same minute. Test your function IsEarlierThan.
|
|
||||||
|
|
||||||
If your IsEarlierThan function is correct, sorting becomes very easy. You just need to pass the
|
|
||||||
function to the sorting routine (make sure to #include <algorithm>). Be sure to study the output
|
|
||||||
and convince yourself things are debugged before asking a TA/mentor for checkoff.
|
|
||||||
sort(times.begin(), times.end(), IsEarlierThan);
|
|
||||||
|
|
||||||
**Importance of const and reference**: After you have debugged and tested this checkpoint, experiment
|
|
||||||
with const and pass-by-reference on the argument types for the function IsEarlierThan. Change them
|
|
||||||
from const pass-by-reference to pass-by-reference w/o the const. Use the -Wall compiler flag to enable
|
|
||||||
all warnings.
|
|
||||||
|
|
||||||
You may see compiler errors/warnings with some OS/compilers. The problem is that the compiler
|
|
||||||
expects the parameters of the comparison function (the 3rd argument to the sort function) to be in
|
|
||||||
a certain form and complains that it can not find the function when the parameters are not in this
|
|
||||||
form. Basically the STL sort function doesn’t want to sort a collection of data if that data is changing
|
|
||||||
during/because of the sorting process!
|
|
||||||
|
|
||||||
Note: Make sure to try this with the g++ compiler as the Visual Studio compiler may not be as
|
|
||||||
strict with const type checking. Also, try IsEarlierThan with pass-by-value parameters. What’s the
|
|
||||||
difference? Switch the function back to const pass-by-reference parameters before asking for a checkoff.
|
|
||||||
-->
|
|
||||||
|
|
||||||
**To complete this checkpoint**: Show a TA your tested and debugged extensions. Be prepared to
|
|
||||||
discuss your implementation. <!--and const and pass-by-reference.-->
|
|
||||||
|
|
||||||
## Checkpoint 4: File IO
|
|
||||||
*estimate: 5-10 minutes*
|
|
||||||
|
|
||||||
In homework 2 and future homeworks, you will see input files which has a unique format: every line of the file has a fixed number of fields, and all these fields are separated by one or more spaces. For input files like this, we encourage you to use the stream operator>> for all input parsing, rather than using the getline() function.
|
|
||||||
|
|
||||||
Following is an example, which is the content of [this input file](input.txt).
|
|
||||||
|
|
||||||
```console
|
|
||||||
Lisa 25 Female 318-125-5013 Undisclosed Undisclosed 41.5833 -83.9274
|
|
||||||
Daniel 27 Male 210-612-4370 Athlete University_of_Pennsylvania 39.0742 -75.9063
|
|
||||||
Brandon 29 Male 580-492-5098 Anesthesiologist University_of_Georgia 42.7252 -72.7156
|
|
||||||
Edward 32 Male 778-799-8211 Firefighter University_of_California_Western_Islands 38.9078 -70.4912
|
|
||||||
Pedro 27 Male 639-312-6466 Undisclosed University_of_California_Desert 40.306 -84.6312
|
|
||||||
Rebecca 30 Female 878-162-8033 Electrician University_of_Kansas 39.6714 -80.2924
|
|
||||||
Isla 33 Female 884-822-9387 Dental_Hygienist Undisclosed 41.2521 -74.7712
|
|
||||||
Brandon 56 Male 357-422-7135 Undisclosed University_of_Chicago 43.5828 -75.3686
|
|
||||||
Isaac 36 Male 446-205-6456 Chemist University_of_Colorado_Boulder 42.8806 -81.55
|
|
||||||
Timothy 46 Male 600-635-3948 Biomedical_Engineer Undisclosed 38.154 -71.3301
|
|
||||||
```
|
|
||||||
|
|
||||||
Each line of this file represents one user from an online dating app. Each line has 8 fields. And these 8 fields are:
|
|
||||||
|
|
||||||
- User's name
|
|
||||||
- User's age
|
|
||||||
- User's gender
|
|
||||||
- User's phone number
|
|
||||||
- User's profession: A user can choose not to disclose their profession, and if so, this field will be Undisclosed.
|
|
||||||
- User's school: A user can choose not to disclose his/her school, and if so, this field will be Undisclosed.
|
|
||||||
- User's current latitude
|
|
||||||
- User's current longitude
|
|
||||||
|
|
||||||
Write a C++ program to read this file and print each user's name and phone number in an output file, the name and the phone number should be separated by a comma. Your output file must be identical as this [sample output file](output.txt).
|
|
||||||
|
|
||||||
You can use the following code snippet. Try to understand the code snippet before you use it.
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
std::ifstream inputFile(filename);
|
|
||||||
|
|
||||||
if (!inputFile.is_open()) {
|
|
||||||
std::cerr << "Failed to open the user data file." << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name, gender, number, profession, school;
|
|
||||||
int age;
|
|
||||||
double latitude, longitude;
|
|
||||||
while(inputFile >> name
|
|
||||||
>> age
|
|
||||||
>> gender
|
|
||||||
>> number
|
|
||||||
>> profession
|
|
||||||
>> school
|
|
||||||
>> latitude
|
|
||||||
>> longitude){
|
|
||||||
|
|
||||||
// do something with any of these 8 fields.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
Lisa 25 Female 318-125-5013 Undisclosed Undisclosed 41.5833 -83.9274
|
|
||||||
Daniel 27 Male 210-612-4370 Athlete University_of_Pennsylvania 39.0742 -75.9063
|
|
||||||
Brandon 29 Male 580-492-5098 Anesthesiologist University_of_Georgia 42.7252 -72.7156
|
|
||||||
Edward 32 Male 778-799-8211 Firefighter University_of_California_Western_Islands 38.9078 -70.4912
|
|
||||||
Pedro 27 Male 639-312-6466 Undisclosed University_of_California_Desert 40.306 -84.6312
|
|
||||||
Rebecca 30 Female 878-162-8033 Electrician University_of_Kansas 39.6714 -80.2924
|
|
||||||
Isla 33 Female 884-822-9387 Dental_Hygienist Undisclosed 41.2521 -74.7712
|
|
||||||
Brandon 56 Male 357-422-7135 Undisclosed University_of_Chicago 43.5828 -75.3686
|
|
||||||
Isaac 36 Male 446-205-6456 Chemist University_of_Colorado_Boulder 42.8806 -81.55
|
|
||||||
Timothy 46 Male 600-635-3948 Biomedical_Engineer Undisclosed 38.154 -71.3301
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "time.h"
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
|
|
||||||
std::cout << "testing constructors" << std::endl;
|
|
||||||
Time a;
|
|
||||||
Time b(13,24,39);
|
|
||||||
Time c(2,5,4);
|
|
||||||
|
|
||||||
std::cout << "testing accessors" << std::endl;
|
|
||||||
std::cout << "a: " << a.getHour() << " " << a.getMinute() << " " << a.getSecond() << std::endl;
|
|
||||||
std::cout << "b: " << b.getHour() << " " << b.getMinute() << " " << b.getSecond() << std::endl;
|
|
||||||
std::cout << "c: " << c.getHour() << " " << c.getMinute() << " " << c.getSecond() << std::endl;
|
|
||||||
std::cout << std::endl;
|
|
||||||
assert (b.getHour() == 13);
|
|
||||||
assert (c.getMinute() == 5);
|
|
||||||
assert (c.getSecond() == 4);
|
|
||||||
|
|
||||||
// UNCOMMENT THESE TESTS AS YOU WORK THROUGH CHECKPOINT 3
|
|
||||||
|
|
||||||
/*
|
|
||||||
std::cout << "testing print" << std::endl;
|
|
||||||
a.PrintAMPM();
|
|
||||||
b.PrintAMPM();
|
|
||||||
c.PrintAMPM();
|
|
||||||
std::cout << std::endl;
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
std::cout << "testing modifiers" << std::endl;
|
|
||||||
a.setHour(4);
|
|
||||||
a.setMinute(32);
|
|
||||||
a.setSecond(1);
|
|
||||||
std::cout << "a: " << a.getHour() << " " << a.getMinute() << " " << a.getSecond() << std::endl;
|
|
||||||
assert (a.getHour() == 4);
|
|
||||||
assert (a.getMinute() == 32);
|
|
||||||
assert (a.getSecond() == 1);
|
|
||||||
a.PrintAMPM();
|
|
||||||
std::cout << std::endl;
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
std::cout << "more testing print" << std::endl;
|
|
||||||
Time noon(12,0,0);
|
|
||||||
Time midnight(0,0,0);
|
|
||||||
Time midnight2(0,0,0);
|
|
||||||
std::cout << "noon ";
|
|
||||||
noon.PrintAMPM();
|
|
||||||
std::cout << "midnight ";
|
|
||||||
midnight.PrintAMPM();
|
|
||||||
std::cout << "midnight2 ";
|
|
||||||
midnight2.PrintAMPM();
|
|
||||||
std::cout << std::endl;
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
std::vector<Time> times;
|
|
||||||
|
|
||||||
times.push_back(Time(0,0,1));
|
|
||||||
times.push_back(Time(12,59,59));
|
|
||||||
times.push_back(Time(23,59,59));
|
|
||||||
times.push_back(Time(1,1,1));
|
|
||||||
times.push_back(Time(22,22,22));
|
|
||||||
times.push_back(Time(12,0,1));
|
|
||||||
times.push_back(Time(11,59,59));
|
|
||||||
times.push_back(Time(12,0,0));
|
|
||||||
times.push_back(Time(23,23,23));
|
|
||||||
times.push_back(Time(0,0,0));
|
|
||||||
times.push_back(Time(13,13,13));
|
|
||||||
times.push_back(Time(10,10,10));
|
|
||||||
times.push_back(Time(2,2,2));
|
|
||||||
times.push_back(Time(14,14,14));
|
|
||||||
times.push_back(Time(3,3,3));
|
|
||||||
|
|
||||||
for (int i = 0; i < times.size(); i++) {
|
|
||||||
times[i].PrintAMPM();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
Lisa,318-125-5013
|
|
||||||
Daniel,210-612-4370
|
|
||||||
Brandon,580-492-5098
|
|
||||||
Edward,778-799-8211
|
|
||||||
Pedro,639-312-6466
|
|
||||||
Rebecca,878-162-8033
|
|
||||||
Isla,884-822-9387
|
|
||||||
Brandon,357-422-7135
|
|
||||||
Isaac,446-205-6456
|
|
||||||
Timothy,600-635-3948
|
|
||||||
218
labs/debugging/README.md
Normal file
218
labs/debugging/README.md
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
# Lab 3 — Memory Diagrams, Testing, and Debugging
|
||||||
|
|
||||||
|
For this lab, you must use a terminal. Do not use IDEs for this lab.
|
||||||
|
|
||||||
|
## Checkpoint 1
|
||||||
|
*estimate: 20-40 minutes*
|
||||||
|
|
||||||
|
Read lecture notes [5.7](../../lectures/05_pointers#57-arrays) and [5.8](../../lectures/05_pointers#58-stepping-through-arrays-with-pointers-array-iterators), and try to understand the example code and run the animations. Ask questions if you don't understand the code.
|
||||||
|
|
||||||
|
Write a function compute_squares that takes 3 arguments: two C-style arrays (not STL vectors), a and b,
|
||||||
|
of unsigned integers, and an unsigned integer, n, representing the size of each of the arrays. The function
|
||||||
|
should square each element in the first array, a, and write each result into the corresponding slot in the second
|
||||||
|
array, b. You may not use the subscripting operator ( a[i] ) in writing this function; instead, practice using
|
||||||
|
pointer arithmetic. Also, write a main function and a couple of test cases with output to the screen to verify
|
||||||
|
that your function is working correctly.
|
||||||
|
|
||||||
|
**To complete this checkpoint**: Show a TA your function, the test cases, and the corresponding output.
|
||||||
|
|
||||||
|
## Checkpoint 2 & 3:
|
||||||
|
|
||||||
|
Checkpoints 2 and 3 are an introduction to using a command line debugger: **gdb** or **lldb**.
|
||||||
|
|
||||||
|
Testing and debugging are important steps in programming. Loosely, you can think of testing as verifying
|
||||||
|
that your program works and debugging as finding and fixing errors once you’ve discovered it does not.
|
||||||
|
Writing test code is an important (and sometimes tedious) step. Many software libraries have “regression
|
||||||
|
tests” which run automatically to verify that code is behaving the way it should.
|
||||||
|
|
||||||
|
Here are four strategies for testing and debugging:
|
||||||
|
|
||||||
|
1. When you write a class, write a separate “driver” main function that calls each member function,
|
||||||
|
providing input that produces a known, correct result. Output of the actual result or, better yet,
|
||||||
|
automatic comparison between actual and correct result allows for verifying the correctness of a class
|
||||||
|
and its member functions.
|
||||||
|
|
||||||
|
2. Carefully reading the code. In doing so, you must strive to read what the code actually says and does
|
||||||
|
rather than what you think and hope it will do. Although developing this skill isn’t necessarily easy, it
|
||||||
|
is important.
|
||||||
|
|
||||||
|
3. Using the debugger to (a) step through your program, (b) check the contents of various variables, and
|
||||||
|
(c) locate floating point exceptions and segmentation violations that cause your program to crash.
|
||||||
|
|
||||||
|
4. Judicious use of std::cout statements to see what the program is actually doing. This is useful for
|
||||||
|
printing the contents of a large data structure or class, especially when it is hard to visualize large
|
||||||
|
objects using the debugger alone.
|
||||||
|
|
||||||
|
## Points, Lines, and Slopes
|
||||||
|
|
||||||
|
The programming context for this lab is calculating and sorting lines by their slope. We could use information
|
||||||
|
in a map program that is plotting bicycle routes. Some users might want to pick routes that avoid the steepest
|
||||||
|
roads. Other users looking for a challenge might specifically seek out the biggest uphill sections!
|
||||||
|
|
||||||
|
Our program juggles a number of source code files that define two helper classes, a 3D Point and a Line
|
||||||
|
connecting two Points.
|
||||||
|
|
||||||
|
Please download the following source code files needed for this lab:
|
||||||
|
|
||||||
|
[point.h](./point.h)
|
||||||
|
[point.cpp](./point.cpp)
|
||||||
|
[line.h](./line.h)
|
||||||
|
[line.cpp](./line.cpp)
|
||||||
|
[roads.cpp](./roads.cpp)
|
||||||
|
|
||||||
|
As well as the following data files:
|
||||||
|
|
||||||
|
[input_a.txt](./input_a.txt)
|
||||||
|
[input_b.txt](./input_b.txt)
|
||||||
|
[input_c.txt](./input_c.txt)
|
||||||
|
[input_d.txt](./input_d.txt)
|
||||||
|
|
||||||
|
## Checkpoint 2
|
||||||
|
*estimate: 20-30 minutes*
|
||||||
|
|
||||||
|
1. Examine the provided files briefly. How are the files related or dependent upon each other? Hint: Look at the #include statements. Read through the comments from the developer about the purpose of each class and function.
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
We have intentionally placed a number of bugs in the program that
|
||||||
|
will cause problems when you attempt to compile and then run these programs.
|
||||||
|
Even if you spot the problems, don’t fix them yet!
|
||||||
|
```
|
||||||
|
|
||||||
|
2. When you’re confident you understand what the original programmer was aiming to do, let’s compile
|
||||||
|
and run the program. Take careful step-by-step notes about every command you run, and every line
|
||||||
|
of the program you add or edit (and why you make that edit). Create a new README.txt file to
|
||||||
|
organize your notes. You’ll need to show these to your TA/mentor to get checked off for today’s lab.
|
||||||
|
3. What command line(s) are necessary to compile these files into an executable? Be sure to enable all
|
||||||
|
recommended warnings for the Data Structures course by using the **-Wall -Wextra** flags. Also use the **-g** flag which will include source code line numbers and other useful information for the debugger.
|
||||||
|
4. The program as provided may not compile without error. Perform the minimal edits to the source code files necessary to remove the compilation ERROR and produce an executable. **IMPORTANT: Do not fix any of the compilation WARNINGs yet.**
|
||||||
|
5. Run the program with each of the provided input data files. Take notes on what appears to be working correctly and if anything looks buggy.
|
||||||
|
6. Now let’s examine those compilation warnings a little more closely. Oftentimes programmers can get lazy and ignore compilation warnings because these warnings aren’t necessarily showstoppers that prevent us from testing and running our program on initial datasets. But some compilation warnings are actually very dangerous and can prevent the program from successfully handling all possible input
|
||||||
|
datasets or even from running at all!
|
||||||
|
|
||||||
|
Here’s a list of the categories for some of the more common compilation warnings we see in the Data Structures course. You should see all or most of these when you compile the provided code.
|
||||||
|
|
||||||
|
- warning: expression result unused / expression has no effect
|
||||||
|
- warning: control reaches / may reach end of non-void function
|
||||||
|
- warning: variable is uninitialized when used here / in this function
|
||||||
|
- warning: comparison of integers of different signs: 'int' and 'unsigned int'
|
||||||
|
- warning: returning reference to local temporary object /
|
||||||
|
reference to stack memory associated with a local variable returned
|
||||||
|
- warning: unused variable
|
||||||
|
|
||||||
|
7. Study the source code referenced by each specific compilation warning. Do you see the cause of the
|
||||||
|
warning? Some or all of these warnings might be actual logic or math bugs that will cause the problem
|
||||||
|
to crash or return bad data for some or all inputs. Do you see the problem? Do you know how to fix it? **IMPORTANT: Don’t fix the problems yet**. And don’t worry if you don’t see the error – just
|
||||||
|
staring at the code is not the only debugging technique nor is it the most effective debugging technique
|
||||||
|
for large programs!
|
||||||
|
|
||||||
|
**To complete this checkpoint**, show a TA your detailed notes on compilation, the minimal edits necessary
|
||||||
|
to produce an executable, and the results of your preliminary testing. Be prepared to discuss your observations
|
||||||
|
and any logic or math bugs that you belive you have found (but not yet fixed!) in the code.
|
||||||
|
|
||||||
|
## Checkpoint 3
|
||||||
|
*estimate: 30-40 minutes*
|
||||||
|
|
||||||
|
Now, we will practice using the debugger to find and fix errors. Today we’ll learn how to use the gcc/g++
|
||||||
|
command line debugger, gdb from the GNU/Linux/Ubuntu or MacOSX terminal. NOTE: On Mac OSX, you
|
||||||
|
are probably actually using the llvm/clang++ compiler, so you’ll use lldb instead of gdb in the instructions
|
||||||
|
below. If you didn’t already install gdb/lldb, review the installation instructions on the course webpage.
|
||||||
|
Many introductory gdb/lldb debugging tutorials can be found on-line; just type e.g., *gdb tutorial* into
|
||||||
|
your favorite search engine. Here are a couple:
|
||||||
|
http://www.unknownroad.com/rtfm/gdbtut/gdbtoc.html
|
||||||
|
http://www.cs.cmu.edu/~gilpin/tutorial/
|
||||||
|
|
||||||
|
And here’s a handy table mapping gdb commands to lldb commands:
|
||||||
|
|
||||||
|
https://lldb.llvm.org/use/map.html
|
||||||
|
|
||||||
|
After you learn and demonstrate to your TA the basics of debugging with gdb/lldb you are encouraged
|
||||||
|
to explore other debuggers, for example, the debugger built into your IDE, but we do not provide those
|
||||||
|
instructions. You may also want to try a graphical front end for gdb:
|
||||||
|
|
||||||
|
http://www.gnu.org/software/ddd/
|
||||||
|
|
||||||
|
After today’s lab you can use your favorite search engine to find basic instructions for other debuggers and
|
||||||
|
figure out how to do each of the steps below in your debugger of choice.
|
||||||
|
Note that in addition to a standard step-by-step program debugger like gdb/lldb, we also recommend the use
|
||||||
|
of a memory debugger (drmemory or valgrind) for programs with dynamically-allocated memory (we’ll soon talk about this in Lectures), or anytime you have a segmentation fault or other confusing program behavior. We’ll work the memory debugger in lab next week! Information about memory debuggers is
|
||||||
|
available here:
|
||||||
|
http://www.cs.rpi.edu/academics/courses/fall23/csci1200/memory_debugging.php
|
||||||
|
|
||||||
|
After today’s lab, you should be comfortable with the basics of command line debugging within your preferred
|
||||||
|
development environment. Keep practicing with the debugger on your future homeworks, and be prepared
|
||||||
|
to demonstrate debugger skills when you ask for help in office hours.
|
||||||
|
1. Identify a program and (as appropriate) a test dataset that has buggy behavior or output.
|
||||||
|
2. Getting started: When you plan to use the gdb (or lldb) debugger to investigate your program you
|
||||||
|
need to make sure you have linked the code with the -g option to add debug information (including,
|
||||||
|
source code line numbers!) to the executable.
|
||||||
|
For example (replacing file1.cpp file2.cpp with your source code):
|
||||||
|
```console
|
||||||
|
g++ -g -Wall -Wextra file1.cpp file2.cpp -o executable.exe
|
||||||
|
```
|
||||||
|
In order to start gdb, type (replacing executable.exe with your program):
|
||||||
|
```console
|
||||||
|
gdb executable.exe
|
||||||
|
```
|
||||||
|
NOTE: You can specify command line arguments to your executable on the gdb/lldb command line
|
||||||
|
OR you can specify those command line arguments a little bit later, when you run your executable
|
||||||
|
inside the debugger. Refer to documentation, e.g.: https://lldb.llvm.org/use/map.html
|
||||||
|
|
||||||
|
3. Now we’re inside the command-line debugger. Type *help* in order to see the list of commands.
|
||||||
|
There are several commands for setting breakpoints. You can set a breakpoint by specifying a function
|
||||||
|
name or a line number within a file. For example:
|
||||||
|
```console
|
||||||
|
break main
|
||||||
|
```
|
||||||
|
sets a breakpoint at the start of the main function. You can also set a breakpoint at the start of a
|
||||||
|
member function, as in:
|
||||||
|
```console
|
||||||
|
break Point::get_x
|
||||||
|
```
|
||||||
|
Finally, to set a breakpoint at a specific line number in a file, you may type:
|
||||||
|
```console
|
||||||
|
break line.cpp:31
|
||||||
|
```
|
||||||
|
Set a breakpoint at some point in your code just before (in order of execution!) you think the first error
|
||||||
|
might occur. Finally, in order to actually start running the program under control of the debugger,
|
||||||
|
you will need to type *run* at the gdb command line.
|
||||||
|
|
||||||
|
4. Stepping through the program:
|
||||||
|
You can step through the code using the commands *next* (move to the next line of code), *step* (enter the function), and *finish* (leave the function). The command *continue* allows you to move to the next breakpoint.
|
||||||
|
|
||||||
|
5. Examining the content of variables:
|
||||||
|
- You can use *print* to see the values of variables and expressions.
|
||||||
|
- You can use the command *display* to see the contents of a particular variable or expression when the program is stopped.
|
||||||
|
|
||||||
|
6. Program flow:
|
||||||
|
The command *backtrace* can be used to show the contents of the call stack. This is particularly
|
||||||
|
important if your program crashes. Unfortunately, the crash often occurs inside C++ library code.
|
||||||
|
Therefore, when you look at the call stack the first few functions listed may not be your code.
|
||||||
|
This does not mean that the C++ library has an error! Instead it likely means that your code has
|
||||||
|
called the library incorrectly or passed the library bad data. Find the function on the stack that is the
|
||||||
|
nearest to the top of the stack that is actually your code. By typing *frame N*, where *N* is the index of
|
||||||
|
the function on the stack, you can examine your code and variables and you can see the line number
|
||||||
|
in your code that caused the crash. Type *info locals* and *info args* to examine the data in the
|
||||||
|
function. Type *list* to see the source code.
|
||||||
|
7. Breakpoint on Variable Change: The last powerful debugger feature we will try today is variable
|
||||||
|
monitoring.
|
||||||
|
Add a new modifier member function to the *Point* class named *set_elevation* that changes the *y* coordinate of a *Point* instance. Create an instance of a *Point* object in the *main* function called *pt*.
|
||||||
|
Use the *set_elevation* function on that instance.
|
||||||
|
You can use the command *watch* to halt the program when a variable or expression changes.
|
||||||
|
For example, type *watch pt.y*. You can try the the *rwatch* command which allows you to break on a read of the value (not just a change).
|
||||||
|
NOTE: Students have recently (in the past term or two) reported difficulty getting watch points to work
|
||||||
|
inside their command line debuggers. So don’t worry if you also cannot get this feature to work today.
|
||||||
|
|
||||||
|
Continue to experiment with these different debugger operations. You can now start to fix the bugs in
|
||||||
|
the program that were hinted at by the compilation warnings. Remember to recompile the program and
|
||||||
|
re-launch the debugger after each change to the source code. Continue to take detailed notes on what edits
|
||||||
|
you make to the provided source code.
|
||||||
|
**When you feel comfortable showing off all these debugger features, put your name in the
|
||||||
|
queue for checkoff for this checkpoint.** The TA/mentor will be asking you to demonstrate these features
|
||||||
|
and discuss the compilation warnings and bugs in the provided code. You will also be asked questions about
|
||||||
|
the other steps in debugging.
|
||||||
|
|
||||||
|
Please note that this lab has only given you a brief introduction to debugging. You will learn much more
|
||||||
|
through extensive practice. In fact, when you go to see one of us — the TAs, the mentors, or instructor —
|
||||||
|
to ask for help in debugging your assignments, we will constantly be asking you to show us how you have
|
||||||
|
attempted to find the problem on your own. This will include combinations of the four steps listed at the
|
||||||
|
start of the lab.
|
||||||
5
labs/debugging/input_a.txt
Normal file
5
labs/debugging/input_a.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
0 0 0 10 2 0
|
||||||
|
0 0 0 15 1 0
|
||||||
|
0 0 0 10 0 0
|
||||||
|
0 0 0 20 3 0
|
||||||
|
|
||||||
7
labs/debugging/input_b.txt
Normal file
7
labs/debugging/input_b.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
0 2 0 30 1 0
|
||||||
|
40 1 0 0 0 0
|
||||||
|
10 0 0 30 3 0
|
||||||
|
0 1 10 0 3 0
|
||||||
|
10 3 0 0 1 5
|
||||||
|
1 1 0 30 0 0
|
||||||
|
0 2 0 30 2 0
|
||||||
7
labs/debugging/input_c.txt
Normal file
7
labs/debugging/input_c.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
0 0 0 30 1 0
|
||||||
|
0 0 0 10 3 10
|
||||||
|
0 0 0 20 3 20
|
||||||
|
0 0 0 30 3 0
|
||||||
|
0 0 0 0 3 0
|
||||||
|
0 0 0 10 3 20
|
||||||
|
0 0 0 0 3 20
|
||||||
15
labs/debugging/input_d.txt
Normal file
15
labs/debugging/input_d.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
0 2 0 30 1 0
|
||||||
|
40 1 0 0 0 0
|
||||||
|
10 4 0 30 3 0
|
||||||
|
0 1 10 0 3 40
|
||||||
|
10 3 20 -25 1 5
|
||||||
|
1 1 0 30 0 0
|
||||||
|
0 2 0 30 2 0
|
||||||
|
-20 2 0 30 1 0
|
||||||
|
40 1 0 10 0 0
|
||||||
|
10 2 0 30 4 20
|
||||||
|
0 2 10 -20 3 30
|
||||||
|
10 3 0 0 2 15
|
||||||
|
1 1 0 30 0 0
|
||||||
|
0 2 0 30 2 0
|
||||||
|
|
||||||
42
labs/debugging/line.cpp
Normal file
42
labs/debugging/line.cpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#include <iomanip>
|
||||||
|
#include <cmath>
|
||||||
|
#include "point.h"
|
||||||
|
#include "line.h"
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to calculate the slope of a Line.
|
||||||
|
std::ostream& operator<< (std::ostream &ostr, const Line &l) {
|
||||||
|
ostr << "Road: "
|
||||||
|
<< l.get_a() << " ----- " << l.get_b()
|
||||||
|
<< " gradient = "
|
||||||
|
<< std::fixed << std::setprecision(1) << std::setw(4)
|
||||||
|
<< gradient(l);
|
||||||
|
return ostr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to calculate the gradient of a Line.
|
||||||
|
double gradient(const Line &ln) {
|
||||||
|
float slope = compute_slope(ln.get_a(),ln.get_b());
|
||||||
|
// convert the slope into a percentage gradient
|
||||||
|
float gradient = 100.0 * fabs(slope);
|
||||||
|
// take the absolute value
|
||||||
|
if (gradient < 0) {
|
||||||
|
gradient * -1;
|
||||||
|
}
|
||||||
|
return gradient;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to compare the gradient of two Lines.
|
||||||
|
// (That can be used to sort a collection of roads.)
|
||||||
|
bool steeper_gradient(const Line &m, const Line &n) {
|
||||||
|
double gradient_m = gradient(m);
|
||||||
|
double gradient_n = gradient(n);
|
||||||
|
if (gradient_m > gradient_n)
|
||||||
|
return true;
|
||||||
|
if (gradient_n > gradient_m)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
29
labs/debugging/line.h
Normal file
29
labs/debugging/line.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include "point.h"
|
||||||
|
|
||||||
|
// A simple line class. In this simple world, we'll follow the
|
||||||
|
// convention often used in Computer Graphics. y is the vertical
|
||||||
|
// axes, "pointing" up. The x and z axes define the ground plane.
|
||||||
|
|
||||||
|
class Line {
|
||||||
|
public:
|
||||||
|
Line(const Point &a_, const Point &b_) : a(a_),b(b_) {}
|
||||||
|
const Point& get_a() const { return a; }
|
||||||
|
const Point& get_b() const { return b; }
|
||||||
|
private:
|
||||||
|
Point a,b;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to print a Line.
|
||||||
|
std::ostream& operator<< (std::ostream &ostr, const Line &l);
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to gradient of a line.
|
||||||
|
double gradient(const Line &ln);
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to compare the gradient of two Lines.
|
||||||
|
// (That can be used to sort a collection of roads.)
|
||||||
|
bool steeper_gradient(const Line &m, const Line &n);
|
||||||
|
|
||||||
|
|
||||||
25
labs/debugging/point.cpp
Normal file
25
labs/debugging/point.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include <iomanip>
|
||||||
|
#include "point.h"
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to print a Point.
|
||||||
|
std::ostream& operator<< (std::ostream &ostr, const Point &p) {
|
||||||
|
ostr << std::fixed << std::setprecision(1)
|
||||||
|
<< "<"
|
||||||
|
<< std::setw(5) << p.get_x() << ","
|
||||||
|
<< std::setw(5) << p.get_y() << ","
|
||||||
|
<< std::setw(5) << p.get_z() << ">";
|
||||||
|
return ostr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to compute the slope between two Points.
|
||||||
|
double compute_slope(const Point &a, const Point &b) {
|
||||||
|
double rise = b.get_y() - a.get_y();
|
||||||
|
double run_x = b.get_x() - a.get_x();
|
||||||
|
double run_z = b.get_z() - a.get_z();
|
||||||
|
double run = sqrt(run_x*run_x + run_z*run_z);
|
||||||
|
double answer = rise / run;
|
||||||
|
return rise / run;
|
||||||
|
}
|
||||||
28
labs/debugging/point.h
Normal file
28
labs/debugging/point.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
// A simple 3D point class. In this simple world, we'll follow the
|
||||||
|
// convention often used in Computer Graphics. y is the vertical
|
||||||
|
// axes, "pointing" up. The x and z axes define the ground plane.
|
||||||
|
|
||||||
|
class Point {
|
||||||
|
public:
|
||||||
|
// CONSTRUCTOR
|
||||||
|
Point(double x, double y, double z) : x_(x),y_(y),z_(z) {}
|
||||||
|
// ACCESSORS
|
||||||
|
double get_x() const { return x_; }
|
||||||
|
double get_y() const { return y_; }
|
||||||
|
double get_z() const { return z_; }
|
||||||
|
private:
|
||||||
|
// REPRESENTATION
|
||||||
|
double x_,y_,z_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to print a Point.
|
||||||
|
std::ostream& operator<< (std::ostream &ostr, const Point &p);
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to compute the slope between two Points.
|
||||||
|
double compute_slope(const Point &a, const Point &b);
|
||||||
68
labs/debugging/roads.cpp
Normal file
68
labs/debugging/roads.cpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <cmath>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "line.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to parse a collection of files from an input file.
|
||||||
|
std::vector<Line>& load(std::ifstream &istr) {
|
||||||
|
std::vector<Line> roads;
|
||||||
|
float x1,y1,z1,x2,y2,z2;
|
||||||
|
while (istr >> x1 >> y1 >> z1 >> x2 >> y2 >> z2) {
|
||||||
|
roads.push_back(Line(Point(x1,y1,z1),Point(x2,y2,z2)));
|
||||||
|
}
|
||||||
|
return roads;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to sort our road collection by gradient (steepest first).
|
||||||
|
void organize(std::vector<Line> &roads) {
|
||||||
|
std::sort(roads.begin(),roads.end(), steeper_gradient);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// A helper function to print data about the collection of roads.
|
||||||
|
void print(const std::vector<Line> &roads) {
|
||||||
|
|
||||||
|
// print each road in the current, sorted order (steepest first)
|
||||||
|
for (int i = 0; i < roads.size(); i++) {
|
||||||
|
std::cout << roads[i] << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// count the number of roads with gradient less than 10%
|
||||||
|
int count;
|
||||||
|
for (unsigned int i = roads.size() - 1;
|
||||||
|
i >= 0 && gradient(roads[i]) < 10.0;
|
||||||
|
i--) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
std::cout << "There are " << count << " road(s) with gradient less than 10%." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This program expects a single argument, the name of the file containing our input data.
|
||||||
|
int main (int argc, char* argv[]) {
|
||||||
|
|
||||||
|
// check the arguments and open the input file for reading
|
||||||
|
if (argc != 2) {
|
||||||
|
std::cerr << "ERROR: Usage: " << argv[0] << " <input_file>" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::ifstream istr(argv[1]);
|
||||||
|
if (!istr.good()) {
|
||||||
|
std::cerr << "ERROR: the file " << argv[1] << " was not successfully opened for reading." << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the data from the input file
|
||||||
|
std::vector<Line> roads = load(istr);
|
||||||
|
|
||||||
|
// sort the roads by gradient, and print information about the roads
|
||||||
|
organize(roads);
|
||||||
|
print(roads);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user