📄 chapter2.htm
字号:
have been added so that circle is a complete class by itself. It is interesting that one of the
main benefits from <a href="chapter1.htm#inheritance">inheritance</a> and <a href="chapter1.htm#polymorphism">polymorphism</a> in C++ will be found here. Careful use
of <a href="chapter1.htm#inheritance">inheritance</a> will reduce the code size. The code in a base class that is not changed by
<a href="chapter1.htm#inheritance">inheritance</a> will be used by all classes inherited from the base. Therefore, the code in
move() will not be repeated in the classes inherited from Graph unless there is something
unique to the class that will not allow use of the base class <a href="chapter1.htm#method">method</a>s.
<pre>
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include "defines.h"
#include "graph.h"
/* elements and <a href="chapter1.htm#method">method</a>s of the class graph */
#define RECTANGLE_CLASS\
GRAPH_CLASS\
void (* resize)(struct rectangle*,int,int);\
int x1;\
y1;
typedef struct rectangle
{
RECTANGLE_CLASS
} Rectangle;
Rectangle* rectangle_(int,int,int,int,int,int);
void rectangle__(Rectangle*);
#endif
</pre>
<p>The class Rectangle remains to be implemented. The basic approach to the
creation of this class is quite similar to that used with Point and Circle above. The header
file is written above, and the <a href="chapter1.htm#implementation_file">implementation file</a> is shown below. Note here that we have
created a special function that is equivalent to a private <a href="chapter1.htm#method">method</a> in C++. This function,
draw_rectangle() is used by both hide() and show(). There is no reason that this function
should be called by any part of the program other than by these two <a href="chapter1.htm#method">method</a>s. Therefore,
this static function can be seen in the <a href="chapter1.htm#implementation_file">implementation file</a> only and is hidden from the rest
of the program. Of course, the only way that hide() and show() can be accessed is
through the <a href="chapter1.htm#method">method</a> function pointers contained within the instantations of Rectangle
objects.
<pre>
#include "rectangl.h"
static draw_rectangle(int,int,int,int);
static void hide(Rectangle *this)
{
this->visible=FALSE;
pen_color(this->background);
draw_rectangle(this->x,this->y,this->x1,this->y1);
}
static void show(Rectangle *this)
{
this->visible=TRUE;
pen_color(this->foreground);
draw_rectangle(this->x,this->y,this->x1,this->y1);
}
/* x_new and y_new are the new rectangle sides */
static void resize(Rectangle *this,int x_new,int y_new)
{
Boolean see=this->visible;
hide(this);
this->x1=this->x+x_new;
this->y1=this->y+y_new;
if(see)
show(this);
}
static move(Rectangle *this,int x_new,y_new)
{
int delx,dely;
delx=x_new-this->x;
dely=y_new-this->y;
hide(this);
this->x=x_new;
this->y=y_new;
this->x1+=delx;
this->y1+=dely;
show(this);
}
static draw_rectangle(int x1, int y1,int x2, int y2)
{
move_to(x1,y1); /* start at one corner */
line_to(x2,y1); /* and draw a line around */
line_to(x2,y2); /* the rectangle */
line_to(x1,y2);
line_to(x1,y1);
}
</pre>
<p> The <a href="chapter1.htm#constructor">constructor</a> and <a href="chapter1.htm#destructor">destructor</a> are shown below.
<pre>
Rectangle* rectangle_(int x, int y, int x1, int y1,
int background, int foreground)
{
Graph* temp;
Rectangle* this;
temp=graph_(x,y,background,foreground);
if((this=(Rectangle*) malloc(sizeof(Rectangle)))==NULL)
error_handler();
memmove(this,temp,sizeof(Graph));
graph__(temp);
this->hide=hide;
this->show=show;
this->move=move;
this->resize=resize;
this->x1=x1;
this->y1=y1;
return this;
}
void rectangle__(Rectangle* this)
{
hide(this); /* hide before destroying */
free(this);
}
</pre>
<p>The following test program uses each of the above objects. Note that the various
<a href="chapter1.htm#header">header file</a>s are each included in this program. Within main() the variables foreground and
background are given values corresponding to white and black respectively. An instance
of Point, Circle,and Rectangle is created. The point is at the center of the screen which is
also the center of the circle. The circle has a radius of 80 and the rectangle is placed
around the circle. The rectangle, a square in this case, is made one pixel larger than the
circle in each dimension. The computer is put into a graphics display mode, and these
objects are then shown followed by a getch() which will freeze the image on the screen.
After a key is hit, the rectangle is moved to the right and the circle with its center point is
moved to the left. Then the image is again frozen until a key is hit. After a key is hit the
second time, the rectangle is resized.
<pre>
#include <graphics.h>
#include "point.h"
#include "circle.h"
#include "rectangl.h"
main()
{
int foreground=15,background=0;
Point *pt=point_(320,240,background,foreground);
Circle *cir=circle_(320,240,80,background,foreground);
Rectangle *rect rectangle_(241,321,401,161,background,foreground);
setvmode(VGA_16);
(*pt->show)(pt);
(*cir->show)(cir);
(*rect->show)(rect);
getch();
(*rect->move)(rect,401,321);
(*cir->move)(cir,240,240);
(*pt->move)(pt,240,240);
getch();
(*rect->resize)(rect,100,-100);
getch();
setvmode(DEFAULTMODE);
}
</pre>
<p> All of the numbers in the above example seem fine with the exception of the y
value in the rectangle resize call. In that case, the new y value is -100 rather that what
intuition would say should be 100. The problem here is that the location of the origin of
the screen is in the upper left-hand corner rather than the lower left-hand corner as one
would have with a graph. Therefore, all of the increasing y values will move toward the
bottom of the screen rather than toward the top. If you want the new rectangle to have its
locked corner at its lower left-hand corner as these objects assume, you must use negative
numbers to move toward the top of the screen.
<h2><a name="interface">Toward Better Object Interface</a></h2>
There are two additional considerations that should be examined now. First, we
have made no provisions for the inclusion of an in-line function, and second, there is
another way for the virtual and inherited functions to be written that will allow a more
uniform interface with the object <a href="chapter1.htm#method">method</a>s. Both of these considerations involve the use of
C macros which is an anathema to most C++ programmers. One of the most sacred
considerations that will be found with C++ is the strict type checking that is always in
force. On the other hand, C might seem to have relatively strict type checking, but in
reality, there are areas in which the type checking is completely lax. The most prominent
place where type checking falls down in C is through the macro. A macro is a character
substitution scheme. With macros, constants can be defined, but these constants are
nothing more that character strings that are substituted at the time of compilation. The
type of the constant is then taken in terms of the context of its use in the program. There
is no rigidly defined type for any defined constant.
<p> Similarly, functions defined by macros are also completely untyped. As such, the
programmer can get into trouble very easily when using macro defined functions. As an
example, consider the following macro definition for a simple function:
<pre>
#define max(a,b) ((a)>(b)) ? (a) : (b)
</pre>
<p>The clear intent of this function is to determine the parameter with the largest value and to
return this value to the calling program. So long as both a and b are of the same type, the
function works well. The return will be of the same type as both a and b. Suppose
however, that the program sent a type int for a and a float for b. In this case, when a is
larger than b, an int will be returned to the calling function. When b is greater that a, max
will return a float! We cannot tell for certain the type of the return from a macro, even
though the calculations that take place will be correct. The macro is rarely used in C++,
but it is still a convenient device for C programming. Just be careful when using macros
and remember that they can be the source of some difficult problems when you are not
careful with the types that are being used for parameters to the macro functions.
<p>Let us go back and revisit the program saved in counter2.h and counter2.c These
files are listed below for convenience.
<pre>
#ifndef COUNT_H
#define COUNT_H
#include "defines.h"
#define COUNT \
WORD count; \
void (*increment)(struct cnt *);\
WORD (*query)(struct cnt *);
typedef struct cnt
{
COUNT
}Count;
Count *count_(void);
void count__(Count *);
#endif
</pre>
<p>The <a href="chapter1.htm#implementation_file">implementation file</a> for this class is shown below.
<pre>
#include "counter2.h"
#include <stdlib.h> /* needed for the memory functions */
static void increment(Count *this)
{
this->count++;
}
static WORD query(Count *this)
{
return this->count;
}
Count *count_(void)
{
Count *this;
this=(Count *)malloc(sizeof(Count));
this->count=0;
this->increment=increment;
this->query=query;
return this;
}
void count__(Count *this)
{
free(this);
}
</pre>
<p> We will make a few simple changes in these files that simplify the use of the
<a href="chapter1.htm#method">method</a>s of the object. First,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -