Phil's guide to object-oriented ANSI C, part 2

(Continued from part 2)


Inheritance

It is even possible to do a limited form of inheritance with this data model.
(An inherited class, is a class that "Inherits" most of its functionality and structure from another class. This is called a "derived", or "child" class. The reason for doing this is usually that you wish to have a collection of objects that are mostly the same, but have some minor differences in behaviour. Or, someone else has written a class that is Almost-but-not-quite exactly what you need yourself.)

What you have to do is ensure that any additional data from a derived class, comes "after" the old inherited data. Then you can safely "cast" the object from the derived class, to the original class, and all the original member functions will work safely.

Thus, using the old "struct fooobj" from the example above, we then have the ability to construct a SuperFooOBJ, as follows.

Note that you can "inherit", without changing one byte of code from the original FooOBJ implementation. Depending on just how you handle your "constructors", you can even do this sort of thing while keeping the parent FooOBJ data type completely opaque as well!
(in tricky cases, doing nasty things with malloc/memcpy of the old parent data area, so that deleteFooOBJ() has something to free() )

Warning: If you read the comments and accompanying constructor/destructor code, you may realize that it would be safest and quickest for inheritance support, to ensure that constructors are not allowed to use malloc() or equivalent.

(Unfortunately, I think I have forgotten the completely clean, all-compilers-safe bulletproof way to handle the data/struct allocation, but this seems to prove the concept well enough. The code runs, at least :)

SuperFooOBJ.h


typedef struct superfooobj *SuperFooOBJ;

SuperFooOBJ newSuperFooOBJ();
void deleteSuperFooOBJ(SuperFooOBJ);

void setSuperFooStyle(SuperFooOBJ, int);
void dumpSuperFooStyle(SuperFooOBJ);

SuperFooOBJ.c


#include 
#include "FooOBJ.h"
#include "SuperFooOBJ.h"

/* You could do this part more cleanly by having a shared
 *   "FooOBJ-private.h"
 */
struct fooobj {
	int privateint;
	char *privateString;
	/* Depending on your preferences, you
	 * may prefer privateString to be a char buffer[],
	 * OR malloc it and free on delete.
	 */
};

/*
 * Beware optimization here!
 * There are fancier ways to use "union", so that it ensures
 * the parent database always appears "first", but I forget them.
 * So "union" here is used mostly as a reminder.
 */
struct superfooobj {
	union {
		struct fooobj fobj;
	} parent;

	union {
		int mystyle;
	} me;
};



SuperFooOBJ newSuperFooOBJ(){
	SuperFooOBJ foo=(SuperFooOBJ)malloc(sizeof(struct superfooobj));
	bzero(foo, sizeof(struct superfooobj));
	return foo;
	/* If we knew parent obj did more than just zero out memory,
	 * we could get more "interesting" here.
	 * But then deleting would get trickier still.
	 */
}

void setSuperFooStyle(SuperFooOBJ obj, int i){
	obj->me.mystyle=i;
}

void dumpSuperFooStyle(SuperFooOBJ sobj){
	/* Technically, we could access the parent class data
	 * "directly", but in this case we dont need to.
	 */
	if(sobj==NULL) return;
	dumpFooState((FooOBJ)sobj);
	printf("value of super style==%d\n", sobj->me.mystyle);

}

void deleteSuperFooOBJ(SuperFooOBJ foo){
	/* now the tricky bit.. we cant just "free" the
	 * the parent type object, if th parent does any mallocs in its
	 * constructor.
	 * If we DID, then we may need to do things like
	 *     free(foo->parent.privateString);
	 */
	free(foo);
}

test2.c


#include "FooOBJ.h"
#include "SuperFooOBJ.h"


void diddle(FooOBJ obj){
	/* Perform member functions on FooOBJ.
	 * If you try these functions on a different type of object,
	 * you will get a compile-time error
	 */
	puts("diddle setting values of FooOBJ");
	setFooNumber(obj, 1);
	setFooString(obj, "somestring");
}

int main(){
	SuperFooOBJ sobj;
	sobj=newSuperFooOBJ(); /* create a new object of type "SuperFooOBJ"*/
	puts("---main(): dumping values of SuperFooOBJ");
	dumpSuperFooStyle(sobj);

	diddle((FooOBJ)sobj);

	puts("---main(): dumping values of SuperFooOBJ");
	dumpSuperFooStyle(sobj);

	puts("---main(): setting tyle of SuperFooOBJ");
	setSuperFooStyle(sobj, 111);

	puts("main(): dumping values of FooOBJ");
	dumpSuperFooStyle((FooOBJ)sobj);

	deleteSuperFooOBJ(sobj);


	return 0;
}


Author: phil@bolthole.com
Part of bolthole.com... OO C tutorial ... ksh tutorial ... AWK Programming tutorial