Point
defined in dbpoint.h
and dbpoint.cpp
. You
can use this point class to track position.
Your program will read from the user info on a single boat. Specifically, you will be given its name, position (as an (x,y) pair giving its position in miles from some fixed point), its heading (in degrees from north) and its speed in miles per hour. It will then read a number of hours from the user and update the boat's position after that number hours. It will keep reading number of hours and printing over and over for as long as the user wants.
This function might also help you:
Point move(Point position, double heading, double speed, double t) { double PI = 3.14159; Point d; d.x = t*speed*sin(heading*PI/180); d.y = t*speed*cos(heading*PI/180); return position + d; }
Enter boat info: Slowpoke (140.3,33.7) 37.0 degrees 10 mph Currently: Slowpoke (140.3,33.7) 37 degrees 10 mph Time ellapsed in hours: 0.5 Currently: Slowpoke (140.601,34.0993) 37 degrees 10 mph Time ellapsed in hours: 2.5 Currently: Slowpoke (142.105,36.0959) 37 degrees 10 mph Time ellapsed in hours: quitRequirements: You must make a class
Boat
that stores all the necessary information about a boat, and
make all the work (like reading and writing and moving the
boat) into functions. There should be a file
Boat.h
and Boat.cpp
that implements
all of this stuff. Your main
function should be
very simple, and should just include Boat.h
.
If you do this right, it would be trivial to transform this
program so that it would track many boats simultaneously!
Turn In: A screen capture of your program running, and
a printout of Boat.h
, Boat.cpp
and
main.cpp
.
#include
.
This lecture we'll look into more detail about how that's
done, the organization benefits of doing it, and a few of the
pitfalls.
bool f(int);
in one
place and string f(int);
in another.
These are the solid C++ rules, here's the Golden Rule however:
Every prototype or class definition should exist only in one
file. If you need that class or function in several files, put
the class definition or prototype in a .h
file
and use #include
to put them in other files. This is
wisdom, pay attention to it!
Now, there's one trick that can come up, which is illustrated by the following example:
point.h | triangle.h | main.cpp |
class point { public: double x, y; }; void read(point&); |
#include "point.h" class triangle { public: point vertex[3]; }; void read(triangle&); |
#include "point.h" #include "triangle.h" int main() { triangle T; read(T); point P = T.vertex[0]; } |
point
to be
defined twice in main.cpp
. Why? Well,
point.h
includes the definition, and
point.h
is #include
d in
main.cpp
, so that's the first definition of point
in main. However, main also #include
s
triangle.h
, which in turn #include
s
point.h
, which has the definition of the class
point
, so that's the second definition of point in main.
Admittedly, if the programmer was on top of things he could've
avoided this, but it's not always avoidable, and it's expecting
alot of the guy who uses classes point
and
triangle
to go back and worry about this stuff.
The way around this problem is to guard your
.h
files with #define
s.
The #include
that we've been using and the
#define
that we're about to use are examples
of "preprocessor directives". Pages 13-17 in your book talk
about them in more detail. The #define
lets
you give a string x and say "every time you see x
in the source code, replace it with string y." This can
be used and abused in all sorts of ways, but for the moment
we'll simply use #define
to determine
whether or not this is the first time the compiler has seen our
.h
while compiling a give
.cpp
file. We can test whether or not a
certain string has been #define
d already
using the #ifndef
preprocessor directive,
which we read as "if not defined".
For example
cout << "This is "; #ifndef SILLYSTYRING cout << "not "; #endif cout << "much fun!" << endl;If
SILLYSTRING
has been #define
d
previously in our program, the word not
won't get
printed out --- the #ifndef SILLYSTRING
asks
if it's true that SILLYSTRING
hasn't been defined. If
it is, then all the text until the #endif
is seen by
the compiler. Otherwise, as in our case, it is as if the code in
between was never there.
How does this help us with our multiple-definition problem?
point.h | triangle.h | main.cpp |
#ifndef POINTHEADER #define POINTHEADER class point { public: double x, y; }; void read(point&); #endif |
#include "point.h" class triangle { public: point vertex[3]; }; void read(triangle&); |
#include "point.h" #include "triangle.h" int main() { triangle T; read(T); point P = T.vertex[0]; } |
main.cpp
we first
#include
the file point.h
. Since this
is the first time the compiler has seen point.h
it'll find that POINTHEADER
has not been defined.
Therefore all the code until the #endif
, which is
essentially the whole file, will be seen and evaluated by the
compiler. This will include the line that #define
s
POINTHEADER
. Now, the next time we come across
point.h
, which is when main.cpp does the
#include "triangle.h"
, the compiler will hit that
#ifndef
line and will find that
POINTHEADER
has been defined, and therefore
everything up to the #endif
, which is essentially
the whole file, will be ignored by the compiler. This way we
never get any multiple definitions.
Golden rule number 2: All .h
files should
be "protected" by #ifndef
s this way!
load
source, where source is
either keyboard
or a filename, depending on
whether the user will enter the committee info by hand or
read it from a file. From a file or from the user, the
format should be committee name on the first line,
committee chair name on the second line, and the other 4
committee member names on the following lines.
makechair
first last, where
first last is the name of the new chair.
writeminutes
destination
date minutes
endminutes
The minutes should be written to the file
destination, and should begin with the date, the
comittee name, the chair, and the rest of the committee in
alphabetical order. Dates should be input in mm/dd/yyyy
format, but should appear in the minuts as
<day> <monthname>, <year>
save
filename
The file saved should be in the proper format to read in
the next time.
So, suppose we have the following data file
comm.txt, which contains a definition of
the very powerful and important "Committee on Confusing Midshipmen".
This run of the program along with the minutes file it creates,
and the new version of comm.txt
it makes.
Program: | load comm.txt makechair Chris Brown writeminutes CCM110802.txt 11/08/02 New Chairman Brown wrested control of the Committee from the senior faculty! endminutes save comm.txt quit |
CCM110802.txt | 8 November, 2 Minutes of the Committee on Confusing Midshipmen Chris Brown, Committee Chair Committee Members: Ric Crabbe Margeret MacMahon Don Needham Kay Schulze Minutes: New Chairman Brown wrested control of the Committee from the senior faculty! |
comm.txt | Committee on Confusing Midshipmen Chris Brown Kay Schulze Don Needham Margeret MacMahon Ric Crabbe |
Admittedly, this is a big problem! We'll need all of our problem solving techniques --- top-down design, bottom-up design, iterative refinement. They all work together, and breaking code up into files helps facilitate all of it.
Committee
that
stored all the info for a committee, and functions
read
and write
for reading and writing
committees, both to-and-from files and to-and-from the screen".
If I had such things, perhaps in a file Committee.h
,
I could write my main like this:
#include "Committee.h" int main() { Committee C; string command; while(cin >> command && command != quit) { ////// LOAD COMMAND ////// if (command == "load") { string source; cin >> source; if (source == "keyboard") read(C,cin); else { ifstream fin(source.c_str()); read(C,fin); } } ///// SAVE COMMAND ///// if (command == "save") { string name; ofstream fout(name.c_str()); write(C,fout); } } return 0; }Clearly what we need to do is go off and give the class definition and function prototypes in a file
Commitee.h
, and later
we can get around to actually implementing the functions in a file
Committee.cpp
. Let's focus on defining the class
Committee
first, since this is needed for the two
prototypes. Well ... a committee has a name, which we'll store in
a string, and it has five comittee members, one of whom is the Chair.
A committee member has a first and a last name, so there's no
built-in type to represent that for me. So, I say "I wish there
was a type Member
that could store a first and a last
name for me ... and I'll probably want read and write functions
for this type, as well." Well, let's assume there is! (It's
.h
file might look like
Member.h and, in bottom-up fahion, whose
.cpp
file might look like
Member.cpp) We'll define
Committee
as:
class Commitee { public: string name; Member chair, mems[4]; };So that a Committee consists of a name, a chair and 4 members. Additionally we want functions for reading and writing, whose prototypes would be:
void read(Member&,istream&); void write(Member,ostream&);Now, the class
Member
comes from the
Member.h
header file, so our Committee.h
header file looks like
/*************************************** ** Header file for class Commitee and ** associated functions ***************************************/ #include "Member.h" #ifndef COMMITTEEHEADER #define COMMITTEEHEADER class Committee { public: string name; Member chair, mems[4]; }; void read(Committee&,istream&); void write(Committee,ostream&); #endifPutting the whole program together, we have:
Member.h ----------------------> Member.cpp | | | | V | Committee.h ---> Committee.cpp | | | / | | / V | / main.cpp | / \ | / \ | | \ V V ------------> THE PROGRAM
if (command == "makechair") { Member M; read(M,cin); makechair(C,M); }In other words, you read in a member and say "I wish there were a function
makechair
that could modifiy the committee
to make a ne member the chair" ... and then assume there is!
The prototype for makechair
would naturally be added
to Committee.h
and the definition of the function
would naturally reside in Committee.cpp
. The
structure of the program in terms of files looks just like it did before.
.h
file and have its own .cpp
file as well.
In main.cpp
we'll add the code for our next command,
which will "assume" we have a function "writeminutes
",
because gee wouldn't it be easier if we did?
if (command == "writeminutes") { string name; cin >> name; ofstream fout(name.c_str()); writeminutes(C,fout); }Of course, now we have to go and implement
writeminutes
and create Date.h
and Date.cpp
. When we
do, we end up with a program that, on the file level, is
structured like this:
Member.h ---------------------------------> Member.cpp | | | Date.h ----> Date.cpp | | | | | V V | | Committee.h ---> Committee.cpp | | | | | / | | | / V | | / main.cpp | / / \ | / / \ | / / \ V \/ / ------------> THE PROGRAM <--------