Another way to define your own types in C++ is using the
typedef keyword. Actually, this doesn't let you
really make a new type, but rather give a name to an existing
type. So for example the line
typedef double** matrix;would define a new type
matrix,
which is a 2D-array of doubles.
This clearly isn't nearly as exciting as structs
though, since it's really just a way to define shortcuts
from one typename to another one. But even simple typedefs can
be useful to make code more readable. For instance the definition
typedef int card;might have been useful in the project and labs where we represented playing cards as integers, to make it clear which
ints
in our code are actually numbers, and which ones are just referring
to playing cards.
midpoint that
takes two points and returns their midpoint.point that encapsulated both the x and y
coordinates --- it's prototype would be
point midpoint(point a, point b);. It
would be natural to sort 20 Midshipmen ordered by alpha codes
(which would order by class year) if there were a type
mid that encapsulated both alpha code and name
--- I'd have an array mid *A = new mid[20].
Finally, it'd be natural to store student names along with
homework info if there was a type student ---
I'd just store it in an array of student
objects. Clearly, all of these problems scream out for the
ability of the user to wrap up one or more existing types
into one package and call it a new type. In C++,
struct is the mechanism that allows you to do this.
pointpoint would make
such a function simple and natural. We need to wrap up a
double for the x-coordinate and a
double for the y-coordinate into a single
object of a new type - point. Here's how that's
accomlished in C++:
struct point <--- Declares a new type called "point"
{
double x, y; <--- Says that a "point" contains two doubles named x and y
}; <--- Don't forget the ;
This struct definition, like function definitions, appears
outside of main or of any other function definitions,
and it must appear before you try to use an object of type
point. From the point of this definition onwards you
can use point as a new type. If you want to access
the double x within a point object named
P, you write P.x --- note that P.x
is an object of type double, so anything you can do with a double
you can do with P.x!
Moreover it is an l-value, it can be assigned to, passed
by reference, etc. The objects packeged together
in a new struct are called data members.
We'll start off simple by creating an object of type
point, reading values into the object, and printing
it out:
int main()
{
// Creates an object P of type point
point P;
// Reads & stores coordinate values
cout << "Enter x-coord: ";
cin >> P.x;
cout << "Enter y-coord: ";
cin >> P.y;
// Writes out point P
cout << "Point is (" << P.x
<< ',' << P.y << ")" << endl;
return 0;
}
One very important thing to note here is that we can't say
cin >> P in order to read into point
P. Nor can we say cout << P
in order to write out point P.
Why? Because cin and cout know
nothing about the type point!
On the other hand, cin >> P.x works perfectly well,
because cin is just reading into a
double, which we know it does just fine.
Now, let's look at defining the function midpoint:
point midpoint(point a, point b)
{
point m;
m.x = (a.x + b.x)/2;
m.y = (a.y + b.y)/2;
return m;
}
Hopefully this code is pretty much self-explanatory. Notice that
by wrapping up two doubles in the type
point I can, in a sense, return two objects from a
function! Take a look at this complete
program that reads two points from the user and prints out
their midpoint.
midpoint(P,Q).
struct, cin doesn't know how to read that
type, and cout doesn't know how to write that type.
In fact, the compiler doesn't know how to do any of the implicit
or explicit conversions with your new types, so that neither
double(P), where P is an object of type
point, nor point(j), where
j is an object of type int will be
recognized. The only things the compiler knows how to do with your
user defined type are assignment using the =
operator, and copy for pass-by-value arguments in function calls.
These are done by assigning/copying each data member
independently. Most importantly, however, objects of user defined
type are created and detsroyed and passed around just like any
other type: scoping rules are the same, creation with
new is the same, parameter passing is the same,
parameter type matching for function overloading is the same
... all of these things you've already learned still apply.
In fact, the string, ifstream, and
ofstream objects that we've already been using are
structs rather than built-in types.
struct and class.
They are more or less the same (except that in structs members
are public by default, and in classes they are private by
default. Of course that distinction won't make any sense at
this point, but in case you come back and read this later ...),
but historically come from different places. C++
gets struct from C, the language that C++
extends. The term class comes from
"object-oriented programming", which is a style of programming
C++ supports, but which we do not cover in this course. We
are going to use the keyword struct to get you
familiar with what you'll see in the context of C programming
in your Systems Programming course. Of course this means that
in your Objected Oriented Programming course (taught using
Java) the keyword class will be less familiar.
midpoint function with arrays of two
doubles --- believe me, it'd be painful! Where
things really get interesting is when we wrap up objects of
different types in one object, because there we really can't use
arrays like we could for points. Let's think about our example of
Midhipmen names and alpha codes. We might define a
mid as follows:
struct mid
{
int alpha;
string first, last;
};
Notice that this struct has three data members, one of type
int and two of type string. Let's
consider using this type in the following problem: The file
Mids.txt contains the names and
alpha codes of the Midshipmen in my two sections. I want to write
a program that will read that data and store it in an array for
later processing.
To test what I've done, we'll simply allow the
user to enter an alpha, and we'll return the name of the Mid with
that alpha, or an error message if none is found.
Creating the array and reading in data from the file is easy:
// Create and array of 41 Mids
mid *A = new mid[41];
// Open file
ifstream fin("Mids.txt");
// Read in mids
for(int i = 0; i < 41; i++)
fin >> A[i].alpha >> A[i].last >> A[i].first;
Similarly, if int variable a
holds the value of the alpha code to search for, the code that
does the searching and prints the response is straightforward:
// search for alpha a
int k = 0;
while(k < 41 && A[k].alpha != a)
k++;
// print result of search
if (k == 41)
cout << "No Mid with that alpha was found!" << endl;
else
cout << A[k].first << " " << A[k].last << endl;
Enter triangle vertices: (0,0) (0,1) (1,0) Midpoint triangle verts: (0,0.5)(0.5,0.5)(0.5,0)Notice how my solution defines functions for writing and reading points!
writepoint function a different name though.
n vertices
defining an n-gon, and produces a text file that
we can use with gnuplot to plot the n-gon and
its "midpoint n-gon".
struct to write a program that prints out the
10 youngest congresspeople, first and last
names. Here is my solution.