📄 chapter2.htm
字号:
<p>
It may seem like double jeproady that the pointer to an instance of the object must be used
twice in each <a href="chapter1.htm#method">method</a> call. The first is to get to the individual function, and the second is
to identify the specific object being worked upon. If several objects are defined, the
pointer to the object in the function name can be a pointer to any of the several
instantiated objects. Each object will have a pointers to all object <a href="chapter1.htm#method">method</a>s within the
object definition. Every like object will contain these pointers, and their values will all be
the same. That is one of the inefficiencies of this approach. There is the extra baggage of
pointers to each of the object <a href="chapter1.htm#method">method</a>s contained in each instance of the object. Each
object will therefore be somewhat longer than one would think necessary. We will see in
the next example, however, that there are benefits to be obtained from this seeming waste
of memory space.
<p> A test program is shown below. In this case, two instances of the object are
created. Both of these instances are incremented twelve times, and then the a instance is
incremented an additional six times.
<pre>
#include "counter2.h"
#include <stdio.h>
main()
{
Count *a=count_();
Count *b=count_();
int i;
for(i=0;i<12;i++)
{
(*a->increment)(a);
(*b->increment)(b);
}
for(i=0;i<6;i++)
{
(*a->increment)(a);
}
printf("a=%d and b=%d\n",(*a->query)(a),(*b->query)(b));
count__(a);
count__(b);
}
</pre>
<p> The output from this program is
<pre>
a=18 and b=12
</pre>
<p>
as it should be.
<p> The above class will be counter2. counter2 satisfies the requirements established
earlier. It has the ability to be incremented at arbitrary times, and the number of times that
it was incremented can be determined at any time. The program can create an arbitrary
number of counters, and the interface between the counter and the program is uniform.
Admittedly, the interface is somewhat different depending upon the type of class that has
been used, but once this choice has been made, access to each of the <a href="chapter1.htm#method">method</a>s associated
with the class is uniform. In the final version, the <a href="chapter1.htm#method">method</a> names are hidden within the
scope of the file in which they are created. Therefore, there can be several objects that
have the same <a href="chapter1.htm#method">method</a> names, and there will be no name conflicts in the program. The
version called counter1 will not afford this benefit. In that case, the <a href="chapter1.htm#method">method</a> names are
global and it is easily possible to have name conflicts with different classes in the program.
As mentioned above, the price associated with this <a href="chapter1.htm#method">method</a> name hiding is that each object
must contain pointers to each of the class <a href="chapter1.htm#method">method</a>s making the size of the object larger
than with the approach used in counter1. There is, however, an additional benefit beyond
name hiding. That benefit is that with the <a href="chapter1.htm#method">method</a> used in counter2, it is possible to
implement <a href="chapter1.htm#inheritance">inheritance</a>.
<p> The class counter is a simple recording counter that can be incremented and
queried to determine the number of increments. Suppose that we need to expand the
capabilities of our counter to allow not only increment and query, but also decrement.
There is a very important distinction at this point. This class and the objects instantiated
are counters. The basic nature of the operation of the class is unchanged. If we call this
sysem an up/down counter, we can say without fear of contridiction that up/down counter
is a counter. If a program is to use <a href="chapter1.htm#inheritance">inheritance</a>, the programmer must be able to say that
the class inherited from the super class is of the same basic nature as the super class.
Otherwise, a different programming approach probably should be used. Since an up/down
counter is a counter, we are safe to conculde that up/down counter can inherit the
characteristics of counter and we will see a classic case of class <a href="chapter1.htm#inheritance">inheritance</a>.
<p> The <a href="chapter1.htm#header">header file</a> for the up/down counter is shown below. The up/down counter is
to be inherited from counter2, so counter2.h must be included in the <a href="chapter1.htm#header">header file</a>. Recall
that in counter2.h, a define statement defined COUNT which contained all of the
<a href="chapter1.htm#attribute">attribute</a>s and <a href="chapter1.htm#method">method</a>s needed to implement that version of the counter. In this header,
new structure members are defined for the up/down count. First of all, the members of
COUNT are included in the definition of UDCOUNT. Additionally, the function
prototype for the decrement function is included in the definition. UDCOUNT defines all
of the <a href="chapter1.htm#attribute">attribute</a>s and <a href="chapter1.htm#method">method</a>s of COUNT along with the new decrement function. This
define statement is then used as the argument of the struct udcount which is typedefed as
the new type Udcount. This is our new class and the <a href="chapter1.htm#header">header file</a> is saved in counter3.h.
<pre>
#ifndef UDCOUNT_H
#define UDCOUNT_H
#include "defines.h"
#include "counter2.h"
#define UDCOUNT \
COUNT \
void (*decrement)(struct udcount *);
typedef struct udcount
{
UDCOUNT
}Udcount;
Udcount *udcount_(void);
void udcount__(Udcount *);
#endif
</pre>
<p> The functions increment() and query() used in the definition of counter2 are still valid
functions for this class. Therefore, the new class must define only the function
decrement(). decrement() is the first function defined in the <a href="chapter1.htm#implementation_file">implementation file</a>.
<p> The <a href="chapter1.htm#constructor">constructor</a> is somewhat different from that in counter2. The <a href="chapter1.htm#attribute">attribute</a>s and
<a href="chapter1.htm#method">method</a>s of an instance of Count as defined by counter2 are use as a base class to udcount.
A temporary instance of an object Count will be created by execution of the count_()
<a href="chapter1.htm#constructor">constructor</a>. Dynamic allocation is used via malloc() to create a memory space large
enough to hold our new Udcount object. The contents of the temporary Count object are
then moved onto the newly created Udcount object, and then the temporary Count is
deleted by execution of its <a href="chapter1.htm#destructor">destructor</a>. Finally a pointer to the function decrement is
placed in the proper location in the object, and a pointer to the new object is returned to
the calling program. Pointers to incerment() and query() will be contained in the object
created by the <a href="chapter1.htm#constructor">constructor</a> count_(). Therefore, these <a href="chapter1.htm#method">method</a>s from the base class will be
used as <a href="chapter1.htm#method">method</a>s for Udcount. Also, count_() initializes the count to zero, so when an
object of the type Udcount is instantiated, it will have its own version of decrement() and
will share the code from Count for increment() and query(). This new object will also
have its own location count in which the number of increments less the decrements is
stored.
<pre>
#include "counter3.h"
#include <stdlib.h> /* for memory function access */
static void decrement(Udcount *this)
{
this->count--;
}
Udcount *udcount_(void)
{
Udcount *this;
Count *temp=count_();
if((this=(Udcount *)malloc(sizeof(Udcount)))==NULL)
error_handler();
memmove(this,temp,sizeof(Count));
count__(temp);
this->decrement=decrement;
return this;
}
void udcount__(Udcount *this)
{
free(this);
}
</pre>
<p> The test function shown below demonstrates all operations of Udcount. Two
instances of Udcount are instantiated. These instances are a and b. Both instances are
incremented twelve times, and the values of the results are printed out. Then a is
incremented an additional six times while b is decremented six times.
<pre>
#include "counter3.h"
#include <stdio.h>
main()
{
Udcount *a=udcount_();
Udcount *b=udcount_();
int i;
for(i=0;i<12;i++)
{
(*a->increment)(a);
(*b->increment)(b);
}
printf("a=%d and b=%d\n",(*a->query)(a),(*b->query)(b));
for(i=0;i<6;i++)
{
(*a->increment)(a);
(*b->decrement)(b);
}
printf("a=%d and b=%d\n",(*a->query)(a),(*b->query)(b));
udcount__(a);
udcount__(b);
return 0;
}
</pre>
<p> The output from this program is as follows:
<pre>
a=12 and b=12
a=18 and b=6
</pre>
<p>
which is the expected results. Here Udcount has inherited all of the <a href="chapter1.htm#method">method</a>s and
<a href="chapter1.htm#attribute">attribute</a>s of Count and extends the operation of the object to include a decrement as well
as the increment and query associated with Count. Actually, one could have created a
Udcount with all of the necessary <a href="chapter1.htm#method">method</a>s. However, this approach could require more
code than is needed above because, in a program where both Count and Udcount are
needed, the code for the <a href="chapter1.htm#method">method</a>s in Count would not need to be repeated for Udcount.
This ability to share code is one of the important benefits found with <a href="chapter1.htm#inheritance">inheritance</a>.
Additionally, Udcount is another instance of a counter, and it is used in much the same
way as Count. We have been able to build onto a base class another class that has all of
the base class characteristics with some new characteristics of its own.
<h2><a name="polymorphism"><a href="chapter1.htm#polymorphism">Polymorphism and Virtual Functions</a></a></h2>
With the above background, both <a href="chapter1.htm#polymorphism">polymorphism</a> and virtual functions written in C
can be examined. The basic idea of <a href="chapter1.htm#polymorphism">polymorphism</a> is to allow several functions that
execute similar but different functions for different inherited classes to have the same
name. The choice as to which function will be used by the different classes cannot be
made at the time of linking. This choice must be made when the program is run. Thus,
the name late binding or run-time linking is also assigned to <a href="chapter1.htm#polymorphism">polymorphism</a>. It should be
noted that a virtual funcion is in not really bound at run time. In reality, the means by
which a virtual function is created is to use a level of indirection in the choice of a function
to be executed. Therefore, all of the functions referred to as virtual are really accessed by
pointers to the specific function to be executed rather than by the function name. Let us
examine this concept further in the following discussion.
<p> We begin by defining a class Graph that in essence defines a point. However, this
class is really a dummy class that will never be instantiated with a regular program. Such a
class is called an abstract class; the purpose of this type of class is to be a parent or a
superclass which will be used to inerhit other useful classes. Consider the class shown
below.
<pre>
#ifndef GRAPH_H
#define GRAPH_H
#include "defines.h"
/* elements and methods of the class graph */
#define GRAPH_CLASS\
int x,\
y,\
background, \
foreground; \
Boolean visible,\
(* is_visible)(struct graph*);\
void (* show)(struct graph*),\
(* hide)(struct graph*),\
(* move)(struct graph*, int, int);
typedef struct graph
{
GRAPH_CLASS
} Graph;
Graph* graph_(int x,int y,int background,int foreground);
void graph__(Graph*);
#endif
</pre>
<p>
GRAPH_CLASS is defined and contains all of the members of the class Graph. The
struct graph members then is the single statement GRAPH_CLASS. The above class
definition will be saved in a file graph.h.
<p> It is not necessary to include parameter names in a function prototype as was done
above. In the prototypes for the <a href="chapter1.htm#constructor">constructor</a>s there are frequently several parameters, and
it is a good idea to include some type of descriptive names for each of the parameters. For
example, in the above case, x and y are the coordinates of the graphics point. The
parameter background is the background color and foreground is the foreground color.
With these parameters named in the <a href="chapter1.htm#constructor">constructor</a> prototype, is not necessary to examine the
<a href="chapter1.htm#implementation_file">implementation file</a> to determine the meanings of these several parameters.
<p> The <a href="chapter1.htm#implementation_file">implementation file</a> for this class is shown below. We include the file graph.h
to make available to the program all of the information shown above. The <a href="chapter1.htm#constructor">constructor</a> and
<a href="chapter1.htm#destructor">destructor</a> programs shown later are a part of this file. The code has been moved here to
keep the descriptive text near-by the code. With this version of object management, it is
necessary to pass a pointer to the specific instantiation of an object to each <a href="chapter1.htm#method">method</a>
whenever a <a href="chapter1.htm#method">method</a> is called. Therefore, you will note below that the first argument of
each <a href="chapter1.htm#method">method</a> is a pointer to a type Graph. This pointer selects the specific object that the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -