Sie sind auf Seite 1von 12

Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

Object-Oriented Programming In C

July 01, 1990


URL:http://www.drdobbs.com/object-oriented-programming-in-c/184402190

David Brumbaugh is a software developer at Advanced Information Services, Inc. in Peoria IL. He has a B.A. in computer information science from Judson
College in Elgin, IL. He has been programming in C since 1985. He can be contacted at Computer Express BBS, (309) 688-9789, 1220-2400 Baud, 8,1,N
in the C Language Questions folder.

C programmers have been using something like object oriented programming for years. They called it good modularity.

The classic example of "object-oriented C" is the standard FILE structure and its family of functions fopen, fclose, fread, fwrite, fprintf, etc. Only the
"methods" of the file object, fopen etc., access the members of FILE.

The FILE functions are examples of good, modular, manageable code. A more accurate term for this type of programming is "structure driven". Structure-
driven programs consist of data structures and functions that support them. The difference may only be semantic, but FILE objects don't have any
allowance for inheritance or polymorphism. Structure members and functions that operate on them are not encapsulated into a single object.

Adding More OOPness

This article describes a technique which adds inheritance, polymorphism, and encapsulation to the familiar structure-driven style. The steps of this
technique (listed in Table 1) are chosen to work with a particular implementation of inheritance.

Consider the structures:

struct s1
{ int x;
int y;
};
struct s2
{ int x;
int y;
int z;
};

Suppose there is a structure of type s2 and a pointer to type s1.

struct s1 *s1p;
struct s2 s2s;
s1p = &s2s;

In almost all C compilers, s1p->x would be the same as s2s.x, and s1p->y would be the same as s2s.y. You could say that structure s2 inherited x and y
from structure s1. Any function that expected a pointer to s1 could instead take a pointer to s2 and could correctly address x and y and safely ignore z.

Listing 1 illustrates how to utilize this technique in an easy, self-documenting way. By using #define to define a class, S1, and using this definition to
describe a subclass, S2, we assure that any changes to the S1_CLASS definition are automatically reflected in its subclass S2_CLASS at compile time.

An object is an instance of a class. In Listing 1, C's typedef permits objects to be declared.

Coding Conventions

I observe certain conventions when writing methods for this OOP technique.

The first argument to a method is always a pointer to the object calling the method. Many C++ translators do the same thing.
The first argument to a method is always named this, clarifying references to the calling object.
All program code for a particular class is always in the same .c file. Methods are given exactly the same function name as the pointers to those
methods. These functions are static, so they don't interfere with other functions of the same name in other files.
When writing an abstract base class's methods, write functions for methods that are defined to be subclass implemented. You may simply print a
message to the effect that the method is not available.
All constructors are named in the form new_CLASS(). The only arguments in constructors are for initialization. The template in Listing 2 is the basis
for all constructors. If the constructor is a base class, remove all SUPER_CLASS references from this template.
Destructors have a format that reverses the inheritance process. Destructor names have the form destroy_CLASS(). The first, and usually only,
argument is a pointer to the object being destroyed. The second template in Listing 2 is the general form of a destructor.

1 of 12 2/15/2017 4:31 PM
Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

Prior Art

Eric White described another technique for writing "truly" object-oriented programs in the February issue of The C Users Journal. There are some
differences between the technique I am suggesting and his.

This technique does not require any data structures other than those required by the objects. There is no specific CLASS structure and no specific OBJECT
structure like in White's technique.

This technique does not require the use of any additional functions such as White's message function.

Classes and subclasses are defined using C's #deine directive. Methods are inherited from superclasses in a subclass's constructor, like White's, but no
function is required to register new methods.

There are no separate constructors and destructors for CLASS and OBJECT. Constructors and destructors have more responsibility for inheritance and
polymorphism.

Scope is used to supply a rudimentary form of polymorphism, an issue not directly addressed by White.

The resulting syntax of this technique is closer to C+ + than White's. Compare the following three object-oriented methods of having a circle draw itself.
The first example is C++, the second uses White's technique, and the third uses the technique described here.

1. circle.draw(radius);
2. message(&circle,DRAW,radius);
3. circle->draw(circle,radius);

This similarity to C++ was important to me. Most of the OOP code I have seen in articles has been in C++, and I did not want to have to make a large
mental jump to get from C+ + to code I could use.

An Example Application

Many applications need to deal with lists. Sometimes these lists are arrays, sometimes they are linked lists, some- times they are views of database records.
This example will develop a LIST_CLASS. The goal is to create a class that will allow an application to have uniform access to all types of lists, without the
programmer having to concern himself with how the list is stored.

I developed this object when I needed a selector window. The selector window is used as a menu and chooses a record from a data table. The SELECTOR
object had a LIST pointer as a member. Concrete sub-classes of ARRAY_LIST_CLASS and PINNACLE_LIST_CLASS were both used by the SELECTOR,
fulfilling the 00 requirement that a subclass can be used in place of a superclass.

I chose the Pinnacle library for two reasons. First, it is a good, modular, "structure-driven" library. I was able to add an OO layer to it by encapsulation. The
second reason is availability. Pinnacle is a commercial product, but a free trial disk is available from Vermont Database Corporation. The trial diskette will
suffice if you want to try these programs yourself.

In Listing 3 list.h, defines an abstract base class that more concrete classes can inherit from. It defines the types of things you would normally do with a
list.

The list will be ordered in some way, even if it is only a physical order. The list will have a "top" and an "end". There will be the concept of a "current"
member. The methods in Table 2 are common to all lists.

Some methods, such as seek and total_members, can be written by utilizing other methods. Examine list.c in Listing 3. seek may not contain the most
efficient algorithm for all list types, so it can polymorphed later if necessary. The same can be said of total_members.

From this abstract LIST_CLASS, more concrete classes need to be made. The concrete classes must address two issues. First, how the list is implemented.
Second, what goes into the list.

What goes into the list is determined by the application. This example will be a simple phone list with a first name, last name and phone number for each
entry. The list will be maintained in a last name, first name order. A PHONE_ENTRY structure will be used to hold the data.

How to implement the list is a design decision. For the phone list, a database of some sort may be a logical choice. If the list is temporary, a dynamic array
or linked list may be more practical. This is where the OO advantage comes in. If a decision is made to change from a linked list to a database, the changes
are made within the class, not to the application.

I have chosen to use two list handling methods to illustrate this technique. The first example uses a dynamic array to hold the elements of the list. The
second example uses the Pinnacle relational DBMS to hold the elements.

In Listing 4, arrylist.h and arrylist.c show how a dynamic array can be used to implement the LIST_CLASS. Two new attributes are added to the
LIST_CLASS. The curr attribute is the index of the current member, and tot_members is the number of members in the array. The default methods for
total_members and seek were not efficient so these methods are polymorphed.

In Listing 5 phlist1.h adds a PHONE_ENTRY pointer to hold the list and a sort method to maintain the list in a sorted order. In phlist.c all methods not
previously written are completed.

pinlist.h and pinlist.c in Listing 6 show how a database package can be used to make LIST_CLASS more concrete. Pinnacle is built around the relational
model, so a list is defined to be from one table in a database. Column use is application dependent and will be specified in the more concrete subclass.

phlist2.h in Listing 7 defines which columns from the table will be used. A working buffer is also added for convenience. In phlist2.c the constructor

2 of 12 2/15/2017 4:31 PM
Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

defines which table and database to use. I used the sample database provided with Pinnacle.

Listing 8, testlist.c, is the same for both implementations. The output generated by this program is also identical in both cases.

I used Turbo C 2.0 to test the code for this article. To keep some prototyping I turned off the ANSI warning "Suspicious pointer conversion." I could then
assign any type of pointer to any other type of pointer and still keep some prototyping. To use this technique on a different compiler, you may have to
remove the prototypes entirely.

Conclusion

The technique presented here is not intended to replace object-oriented languages such as C++. Instead, the technique is intended to allow a programmer to
experiment with object-oriented techniques without having to invest in learning a new language.

There are a number of advantages to using this technique to write programs. It allows you to realize most of the benefits associated with OOP. Code and
data are changed in an organized way through inheritance. Data encapsulation makes the internals of the objects transparent. Maintenance and
enhancement are easier because of this encapsulation.

There are some advantages to using this technique over C++ or some other OOL. There are enough differences between C++ and C to make many existing
libraries useless. I have seen examples of library functions having names that are C++ reserved words. I have had C++ translators change the names of the
functions I call and cause link errors with outside libraries. With this technique, I can use an object-oriented style and still have full access to all the C
functions currently in use. This is a significant advantage if one considers the thousands of dollars and man-hours invested in existing libraries.

Changing to any OOP technique carries some disadvantages. There is more overhead when using a derived class because all the code from its superclasses
needs to be linked into the program, even when overridden. Bugs are inherited with features. If a superclass contains a design bug, or an inherited
technique contains a bug, the whole class hierarchy is affected, complicating the debugging of large class hierarchies.

There are some disadvantages to using this technique over C++. Some prototyping may be sacrificed in inheritance, especially if you change the prototype
from one class level to the next. C++ constructors are more automatic.

Finally, this technique isn't strictly portable because it assumes that identical members within different structures are arranged uniformly. Officially, the
arrangement of members within a structure is implementation dependent. However, the arrangement assumed here is the rule, rather than the exception, and
I have successfully tested the technique with two MS-DOS compilers and one UNIX compiler.

Acknowledgements

I would like to thank Bob Pauwels and Mike Yocum of Advanced Information Services for their input to this article.

Listing 1

#define S1_CLASS int x; \


int y; \
int (*read_x)(); \
int (*read_y)();

typedef struct s1 {
S1_CLASS
} S1;

#define S2_CLASS S1_CLASS \


int z; \
int (*read_z)(); \

typedef struct s2 {
S2_CLASS
} S2;

Listing 2

CLASS *new_CLASS() {
SUPER_CLASS *s;
CLASS *this;

/* Construct super class */


s = new_SUPER_CLASS();

/* Allocate memory for this object */


this = calloc(1,sizeof(CLASS));

/* Inherit everything you can from the superclass */


memmove(this,s,sizeof(CLASS);

/* We're done with the superclass's memory */


free(s);

/* Assign methods to object */


this->f1 = f1;

/* Inialize attributes here. Open files, allocate etc.*/

return(this);

3 of 12 2/15/2017 4:31 PM
Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

void destroy_CLASS(CLASS *this) {


/* Free any specific data: */
free(this->p);

/* Close any files specific to this class: */


fclose(this->file);

/* Call the superclass's destructor */


destroy_SUPER_CLASS(this);

void destroy_SUPER_CLASS(SUPER_CLASS *this) {


free(this);
}

Listing 3

/* -------------------- List.H------------------ */
#include <stdio.h>
#include <stdlib.h>

#define LIST_CLASS unsigned (*at_top)(struct list*), \


(*at_end)(struct list*), \
(*is_empty)(struct list*), \
(*find)(struct list *, ...); \
void (*prev)(struct list*), \
(*next)(struct list *), \
(*top)(struct list *), \
(*seek)(struct list *, long, int), \
(*end)(struct list *), \
(*display)(struct list*), \
(*add_member)(struct list*, void *), \
(*replace_member)(struct list *, void *), \
void *(*current)(struct list *); \
long (*total_members)(struct list *), \
(*tell)(struct list *);

typedef struct list {


LIST_CLASS
} LIST;

LIST *new_list();
destroy_list(LIST *);

#define TRUE 1
#define FALSE 0

/* -------------------- LIST.C ------------------*/


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "list.h"

static void not_valid() {


fprintf(stderr,"Operation is not valid for this list\n");
getch();
}

static unsigned is_empty(LIST *this) {


return( this->total_members(this) == 0L);
}

static void seek(LIST *this, long where, int start) {


long count;

switch(start) {
case SEEK_SET:
this->top(this);
for (count = 0; count < where; ++count) {
if ( this->at_end(this) )
break;
this->next(this);
}
break;
case SEEK_CUR:
if (where > 0) {
for (count = 0; count < where; ++count) {
if ( this->at_end(this) )
break;
this->next(this);
}
}
else {
for(count = 0; count > where; ++count) {
if (this->at_top(this) )
break;
this->prev(this);
}

4 of 12 2/15/2017 4:31 PM
Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

}
break;
case SEEK_END:

this->end(this);
for(count = 0; count > where; ++count) {
if ( this->at_top(this) )
break;
this->prev(this);
}
break;
}
}

static long total_members(LIST *this)


{
long thisone, count;
thisone = this->tell(this); this->top(this);
count = 0;
do {
if ( ! this->at_end(this) ) {
++count;
this->next(this);
}
} while( ! this->at_end(this) );
this->seek{ this, thisone,SEEK_SET);
return(count);
}

LIST *new_list() {
LIST *this;

/* Allocate Memory for this Object */


this = calloc(1,sizeof(LIST));
if (this == NULL)
return(NULL);

/* Assign Methods */
this->at_top = not_valid; this->at_end = not_valid;
this->is_empty = is_empty; this->find = not_valid;
this->prev = not_valid; this->next = not_valid;
this->seek = seek; this->top = not_valid;
this->end = not_valid; this->display = not_valid;
this->replace_member = not_valid;
this->add_member = not_valid;
this->current = not_valid;
this->total_members = total_members;
this->tell = not_valid;

return(this);
}

destroy_list{LIST *this) {
free(this);
}

Listing 4

#include "list.h"
#include <alloc.h>
#define ARRAY_LIST_CLASS LIST_CLASS \
/* Index of current member */ int curr; \
/* Total members in array */ int tot_members;

typedef struct array_list {


ARRAY_LIST_CLASS
} ARRAY_LIST;

ARRAY_LIST *new_array_list();
destroy_array_list(ARRAY_LIST *);

/*------------------ ARRYLIST.C ------------------------*/

#include "arrylist.h"

static long total_members(ARRAY_LIST *this) {


return((long) this->tot_members);
}

static unsigned at_top(ARRAY_LIST *this) {


return(this->curr == 0);
}

static unsigned at_end(ARRAY_LIST *this) {


return(this->curr == this->tot_members);
}

static void prev(ARRAY_LIST *this) {


if (this->curr > 0)
--(this->curr);
}

5 of 12 2/15/2017 4:31 PM
Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

static void next(ARRAY_LIST *this) {


if (this->curr < (this->tot_members))
++(this->curr);
}

static void seek(ARRAY_LIST *this, long where, int from) {


switch(from) {
case SEEK_SET:
if (where < this->tot_members)
this->curr = (int) where;
break;
case SEEK_CUR:
if (where > 0) {
if ( (this->curr + (int) where) <
this->tot_members ) {
this->curr += (int) where;
}
}
else {
if ((this->curr - (int) where) > 0) {
this->curr -= (int) where;
}
}
break;
case SEEK_END:
if (where <= this->tot_members) {
this->curr = this->tot_members - (int) where;
}
break;
}
}
static void top(ARRAY_LIST *this) {
this->curr = 0;
}
static void end(ARRAY_LIST *this) {
this->curr = this->tot_members - 1;
}
static long tell(ARRAY_LIST *this) {
return(this->curr);

}
ARRAY_LIST *new_array_list(void) {
ARRAY_LIST *this;
LIST *1;

l = new list();
if (l == NULL)
return(NULL);

this = calloc(l,sizeof(ARRAY_LIST));
if (this == NULL) {
destroy list(l);
return(NULL);
}
memmove(this,l,sizeof(LIST));
free(l);

this->total_members = total_members;
this->at_top = at_top; this->at_end = at_end;
this->prev = prev; this->next = next;
this->seek = seek; this->top = top;
this->end = end; this->tell = tell;
return(this);
}

destroy_array_list(ARRAY_LIST *this) {
destroy_list(this);
}

Listing 5

/* ----------------- PHLIST1.H ---------------*/


#include "arrylist.h"

typedef struct phone_entry {


char last_name[21], first_name[11], phone_no[14];
} PHONE_ENTRY;

#define PHONE_LIST_CLASS ARRAY_LIST_CLASS \


PHONE_ENTRY *data; \
void (*sort)(struct phone_list *);

typedef struct phone_list {


PHONE_LIST_CLASS
} PHONE_LIST;

PHONE_LIST *new_phone_list();
void destroy_phone_list(PHONE_LIST *);

/* ------------------ PHLIST1.C ---------------- */

6 of 12 2/15/2017 4:31 PM
Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

#include "phlist1.h"
#include <string.h>
#include <conio.h>
#include <stdlib.h>

static void phone_list_memory_error(char *fun) {


fprintf(stderr,
"\nMemory Error in Function %s <Press a Key>\n", fun);
getch(); exit(1);
}

static unsigned find(PHONE_LIST *this,char *srch_last_name) {


PHONE_ENTRY *pe;
int orig;

orig = this->curr;
while(! this->at_end(this)) {
pe = this->current(this);
if ( stricmp(pe->last_name,srch_last_name) == 0)
return(TRUE);
else if (stricmp(pe->last_name,srch_last_name) > 0) {
this->curr = orig;
return(FALSE);
}
else
this->next(this);
}
pe = this->current(this);
if ( stricmp(pe->last_name,srch_last_name) == 0)
return(TRUE);
this->curr = orig;
return(FALSE);
}

static display(PHONE_LIST *this) {


PHONE_ENTRY *pe;

pe = this->current(this);
if (pe ! = NULL) {
printf("%-20s, %-10s - %-13s\n", pe->last_name,
pe->first_name, pe->phone_no);
}
}

static void add_member(PHONE_LIST *this, PHONE_ENTRY *pe) {


this->data = realloc(this->data,
sizeof(PHONE_ENTRY) * (this->tot_members + 1));

if (this->data == NULL)
phone_list_memory_error("phone_list: add_member");

memmove(this->data + this->tot_members, pe,


sizeof(PHONE_ENTRY));
++(this->tot_members);
this->sort(this);
}

static void replace_member(PHONE_LIST *this,


PHONE_ENTRY *pe) {
if (this->data != NULL)
memmove(this->data + this->curr, pe,sizeof(PHONE_ENTRY));
}

static PHONE_LIST *current(PHONE_LIST *this) {


if (! this->at_end(this) && this->data != NULL)
return(this->data + this->curr);
else
return(NULL);
}

static int pe_comp(PHONE_ENTRY *pe1, PHONE_ENTRY *pe2) {


int ret;
ret = stricmp(pe1->last_name, pe2->last_name);
if (ret == 0)
return(stricmp(pe1->first_name, pe2->first_name));
return(ret);
}

static sort(PHONE_LIST *this) {


qsort(this->data, (size_t) this->tot_members,
sizeof(PHONE_ENTRY), pe_comp);
}

PHONE_LIST *new_phone_list() {
ARRAY_LIST *al;
PHONE_LIST *this;

al = new_array_list();
if (al == NULL)
return(NULL);

this = calloc(1,sizeof(PHONE_LIST));

7 of 12 2/15/2017 4:31 PM
Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

if (this == NULL) {
destroy_array_list(al);
return(NULL);
}
memmove(this,al,sizeof(ARRAY_LIST));
free(al);

this->find = find; this->display = display;


this->add_member = add_member;
this->replace_member = replace_member;
this->current = current; this->sort = sort;
return(this);
}

void destroy_phone_list(PHONE_LIST *this) {


if (this->data)
free(this->data);
destroy_array_list(this);
}

Listing 6

/* ----------------- PINLIST.H-------------------- */
#include "pinnacle.h"
#include "list.h"

#define PINNACLE_LIST_CLASS LIST_CLASS \


/* Pinnacle Database Object */ DB db; \
/*Pinnacle Database Table */ DBTAB table; \
/* Boolean flags*/ unsigned is_at_top, is_at_bottom;

typedef struct pinnacle_list {


PINNACLE_LIST_CLASS
} PINNACLE_LIST;

PINNACLE_LIST *new_pinnacle_list(char *database,


char *table);

/*------------------ PINLIST.C ------------------ */


#include "pinlist.h"

static long total_members(PINNACLE_LIST *this) {


return((long) DB_CountRows(this->table));
}

static unsigned at_top(PINNACLE_LIST *this) {


return(this->is_at_top);
}

static unsigned at_end(PINNACLE_LIST *this) {


return(this->is_at_bottom);
}

static void prev(PINNACLE_LIST *this) {


if (DB_NextRow(this->table, DBPREVIOUS))
this->is_at_top = FALSE;
else
this->is_at_top = TRUE;
if (this->total_members(this) > 1)
this->is_at_bottom = FALSE;
}

static void next(PINNACLE_LIST *this) {


if (DB_NextRow(this->table, DBNEXT))
this->is_at_bottom = FALSE;
if (this->total_members(this) > 1)
this->is at bottom = TRUE;
this->is_at_top = FALSE;
}

static void top(PINNACLE_LIST *this) {


DB_FirstRow(this->table);
DB_NextRow(this->table,DBNEXT);
this->is_at_top = TRUE;
if (this->total_members(this) > 1)
this->is_at_bottom = FALSE;
}

static void end(PINNACLE_LIST *this) {


DB_ForAllRows(this->table);
this->is_at_bottom = TRUE;
if (this->total_members(this) > 1)
this->is_at_top = FALSE;
}

static long tell(PINNACLE_LIST *this) {


DBROWID thisrow, checkrow;
long position = 0L;

thisrow = DB_CurrentRow(this->table);
this->top(this);

8 of 12 2/15/2017 4:31 PM
Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

checkrow = DB_CurrentRow(this->table);
while(checkrow != thisrow) {
++position;

DB_NextRow(this->table,DBNEXT);
checkrow = DB_CurrentRow(this->table);
}
return(position);
}

PINNACLE_LIST *new_pinnacle_list(char *datab,char *table) {


PINNACLE_LIST *this;
LIST *l;

l = new_list();
if (l == NULL)
return(NULL);

this = calloc(1,sizeof(PINNACLE_LIST));
if (this == NULL) {
destroy_list(l); return(NULL);
}
memmove(this,l,sizeof(LIST)); free(l);

this->db = DB_Open(datab,"rw",0);
this->table = DB_Table(this->db,table);
this->total_members = total_members;
this->at_top = at_top; this->at_end = at_end;
this->prev = prev; this->next = next;
this->top = top; this->end = end; this->tell = tell;

return(this);
}

destroy_pinnacle_list(PINNACLE_LIST *this) {
DB_Close(this->db); destroy_list(this);
}

Listing 7

/* --------------------- PHLIST2.H --------------------- */


#include "pinlist.h"

typedef struct phone_entry {


char last_name[21], first_name[11], phone_no[14];

} PHONE_ENTRY;

#define PHONE_LIST_CLASS PINNACLE_LIST_CLASS \


PHONE_ENTRY pe; \
DBCOL last, first, phone, lastfirst;

typedef struct phone_list {


PHONE_LIST_CLASS
} PHONE_LIST;

PHONE_LIST *new_phone_list();
void destroy_phone_list(PHONE_LIST *);
/* ---------------- PHLIST2.C ------------------------ */
#include "phlist2.h"
#include <string.h>
#include <conio.h>
#include <stdlib.h>

static void phone_list_memory_error(char *fun) {


fprintf{stderr,
"\nMemory Error in Function %s <Press a Key>\n", fun);
getch(); exit(1);
}

static unsigned find(PHONE_LIST *this, char *srch_l_name) {


DBSEARCH sobj; unsigned found;

sobj = DB_SearchObject(this->db, String, srch_l_name, "==");

found = DB_FindNext(this->last,sobj,DBNEXT);
DB_Free(sobj);
return(found);
}

static display(PHONE_LIST *this) {


strcpy(this->pe.last_name,DB_GetString(this->last));
strcpy(this->pe.first_name,DB_GetString(this->first));
strcpy(this->pe.phone_no,DB_GetString(this->phone));
printf("%-20s, %-10s - %-13s\n",this->pe.last_name,
this->pe.first_name, this->pe.phone_no);
}

static void add_member(PHONE_LIST *this, PHONE_ENTRY *pe) {


DB_AddRow)(this->table);
DB_PutString(this->last,pe->last_name);

9 of 12 2/15/2017 4:31 PM
Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

DB_PutString(this->first,pe->first_name);
DB_PutString(this->phone,pe->phone_no);
}

static void replace_member(PHONE_LIST *this,


PHONE_ENTRY *pe) {
DB_PutString(this->last,pe->last_name);
DB_PutString(this->first,pe->first_name);
DB_PutString(this->phone,pe->phone_no);
}

static PHONE_ENTRY *current(PHONE_LIST *this) {


strcpy(this->pe.last_name,DB_GetString(this->last));
strcpy(this->pe.first_name,DB_GetString(this->first));
strcpy(this->pe.phone_no,DB_GetString(this->phone));
return(&(this->pe));
}

PHONE_LIST *new_phone_list() {
PINNACLE_LIST *pl; PHONE_LIST *this;

pl = new_pinnacle_list("fonelist.db","PhoneList");
if (pl == NULL)

return(NULL);

this = calloc(1,sizeof(PHONE_LIST));
if (this == NULL) {
destroy_pinnacle_list(pl);
return(NULL);
}

memmove(this,pl,sizeof(PINNACLE_LIST));
free(pl);

this->last = DB_Column(this->table,"Last");
this->first = DB_Column(this->table,"First");
this->phone = DB_Column(this->table,"Phone");
this->lastfirst = DB_Column(this->table,"LastFirst");
DB_OrderBy(this->lastfirst);
this->find = find; this->display = display;
this->add_member = add_member;
this->replace_member = replace_member;
this->current = current;
return(this);
}

void destroy_phone_list(PHONE_LIST *this) {


destroy_pinnacle_list(this);
}

Listing 8

/************************************************************
Testlist.c - Program to test the list object using a phone
list as an example.
************************************************************/
#include "phlist2.h"

static PHONE_ENTRY test_data[] = {


{"Able","Ben","456-7890"},{"Smith","John","456-0987"},
{"Kirk","Jim","622-1701"},{"Picard","Jon L.","622-1701"},
{"Jones","Cyrano","874-2253"}
};

static PHONE_ENTRY jane = {"Smith","Jane","123/456-0987"};

main()
{
PHONE_LIST *pe;
int x;

pe = new_phone_list();
for (x = 0; x < 5; ++x)
pe->add_member(pe,&test_data[x]);
printf("\nTesting Phone List:\n");
pe->top(pe);

while( ! pe->at_end(pe) ) {
pe->display(pe); pe->next(pe);
}
printf("\n Finding - Kirk \n");
pe->top(pe);
if (pe->find(pe,"Kirk") == TRUE)
pe->display(pe);
printf("\n Trying to find McCoy \n");
if (pe->find(pe,"McCoy") == FALSE)
printf("\nMcCoy not found\n");
printf("Current Member is :\n");
pe->display(pe);
printf("Replace John Smith with Jane\n");

10 of 12 2/15/2017 4:31 PM
Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

pe->top(pe);
if (pe->find(pe,"Smith") == TRUE) {
pe->replace_member(pe,&jane);
}
else {
printf("Not Found (Strike a Key)\n"); getch();
}
printf("\nRedisplaying phone list:\n");
pe->top(pe);
while( ! pe->at_end(pe) ) {
pe->display(pe); pe->next(pe);
}
pe->end(pe);
printf("Total members = %ld\n",pe->total_members(pe));
printf("Current member = %ld\n",pe->tell(pe));
}

Table 1

For any base class:

1. Use the preprocessor #define to define the class.

2. Use the class and typedef to define an object.

3. Write methods common to the entire class hierarchy. A


method must take a pointer to the object calling it as
the first parameter.

4. Write the constructor.

a. Allocate memory for the object.

b. Assign methods by setting function pointers to the


methods written in step 3.

c. Initialize attributes. This includes the allocation of


additional memory, the opening of files, etc.

5. Write the destructor.

a. Close any files opened in constructor.

b. Free any additional memory allocated by constructor.

c. Free this object.

For any subclass:

1. Use superclass's definition, then add new methods and


attributes.

2. Write any methods that are different from the


superclass's method.

3. Write the constructor.

a. Call the superclass's constructor.

b. Allocate memory for this object.

c. Copy the superclass to this object.

d. Free the memory used by the superclass. Use free(), not the
destructor.

e. Assign the new/different methods written in step 2.

f. Initialize attributes. This includes the allocation of


additional memory, the opening of files, etc.

4. Write the destructor.

a. Close any files specific to THIS object.

b. Free only memory specific to THIS object.

c. Call the superclass's destructor.

Table 2

METHOD PURPOSE
------------------------------------------------------------------------

at_top Return TRUE if current member is top member.

at_end Return TRUE if current member is last member.

is_empty Return TRUE if LIST is empty, FALSE otherwise.

11 of 12 2/15/2017 4:31 PM
Dr. Dobb's | Object-Oriented Programming In C | July 01, 1990 http://www.drdobbs.com/article/print?articleId=184402190&siteSecti...

find Search the list for an implementation defined member.


If not found don't change currency.

prev Make the member previous to this one current. If current


member is top, do nothing.

next Make the member after this one current. If current


member is last, do nothing.

seek Search to a position in the list. Use like fseek.

top Make the top member current.

end Make the last member current.

display Display the current member.

add_member Add a new member to the list.

replace_member Replace data in current member.

current Return a pointer to the current member.

total_members Return the total number of members int the list.

tell Return the position, from the start of the list of


the current member. The top member is 0.

Terms of Service | Privacy Statement | Copyright 2017 UBM Tech, All rights reserved.

12 of 12 2/15/2017 4:31 PM