OOFILE | Downloads | Purchasing | Press | Services | Company Information | Soapbox | References | F.A.Q. | HOME

OOFTST 19 - mixin inheritance

This sample demonstrates using an abstract base class and using mixin inheritance  


#include "oofile.h"	// the general oofile library

Firstly, we declare a small, simple class to demonstrate a mixin class. A mixin class is a set of functions bound together into a class (usually used to keep related functions together). It is included in other classes, but is never derived from and never instantiated. It's kind of like having a box to put things in that have nowhere else to go, but you want to keep them together.

class mixinSomeFields
{
public:
	dbChar Recipient;
	mixinSomeFields() :
		Recipient(80, "Recipient")
	{};
};

Our next class is to be our abstract one. The use of the pure virtual function FormattedName() is what makes it abstract.

Testing: declaring abstract class

DECLARE_ABSTRACT_CLASS(dbFileBase)
public:
	dbChar		FileName;
	dbFileBase(const char* tableName) : dbTable(tableName),
		FileName(255, "File Name") 
	{};

This is where we declare our virtual function. The use of =0 makes it a pure virtual function and so this becomes an abstract class. (=0 can be thought of as meaning that the function has a NULL pointer, ie no code exists for this function, yet).

Testing: declaring pure virtual function

	virtual void FormattedName(ostream&)=0;
};
  

Now we declare our first concrete, derived class. You can see that we have inherited from our abstract class and so we must override the pure virtual function FormattedName() or this class will not be concrete, but abstract also.

Testing: inheritance from abstract class

class dbIncoming : public dbFileBase
{
public:

As we inherit from other than dbTable, must use the following macro instead of DECLARE_CLASS.

	OOFILE_METHODS(dbIncoming)

Here we have the function that we must override. Note that we still declare it as being virtual. Although the compiler will pick it up anyway, if we explicitly state that it is virtual, we will remind ourselves that it still is.

	virtual void FormattedName(ostream&);
	dbIncoming() : dbFileBase("Incoming") {};
};

Now we declare our second concrete derived class. This one inherits from the abstract class (meaning we must override the pure virtual function FormattedName()) but also inherits our mixin class. note that nowhere else is our mixin class used.

Testing: inheritance from abstract class and mixin class.

class dbOutgoing : public dbFileBase, public mixinSomeFields
{
public:

Because we use multiple inheritance, we must use the following macro instead of DECLARE_CLASS.

	OOFILE_METHODS(dbOutgoing)

Here we have the function that we must override. Note that we still declare it as being virtual. Although the compiler will pick it up anyway, if we explicitly state that it is virtual, we will remind ourselves that it still is.

	virtual void FormattedName(ostream&);
	dbOutgoing() : dbFileBase("Outgoing"), mixinSomeFields() {};
};

Now we override the pure virtual functions. For our first derived class, we just have it print one of the fields.

Testing: overriding pure virtual class

void 
dbIncoming::FormattedName(ostream& os)
{
	os << FileName << endl;
}

For our second derived class, we print one of the fields from our class and also the field from the mixin class this class inherited.

Testing: overriding pure virtual class - including mixin fields

void 
dbOutgoing::FormattedName(ostream& os)
{
	os << FileName << " sent to " << Recipient << endl;
}
  
int main()
{

The first thing we do is to declare our global variables for the database and each table.

	TEST_CONNECT   	theDB;
	dbIncoming     		Incoming;
	dbOutgoing			Outgoing;  
	cout << "OOFILE Validation Suite - Test 19\n"
		 << "Simple test to demonstrate inheritance" << endl; 

We determine our platform and assign a meaningful filename accordingly.

	#ifdef TESTING_DBASE
		#ifdef _Macintosh
			const char* kExistsName =  ":ooftst19:Incoming.dbf";
			const char* kDatabaseName = ":ooftst19:";
		#else
			const char* kExistsName =   "Incoming.dbf"
			const char* kDatabaseName = "";
		#endif	  
	#else
		const char* kDatabaseName = "ooftst19.db";
		const char* kExistsName = kDatabaseName;
	#endif  

We test for the file, if it exists, we open it, if it doesn't, we create it and add some test data to each table.

	if (dbConnect::fileExists(kExistsName)) {
		theDB.openConnection(kDatabaseName);
	}
	else {
		theDB.newConnection(kDatabaseName);
		Incoming.newRecord();
		Incoming.FileName = "An Incoming File";
		Incoming.saveRecord();
		Outgoing.newRecord();
		Outgoing.FileName = "An Outgoing File";
		Outgoing.Recipient = "XXX";
		Outgoing.saveRecord();
	}  

Now we invoke the virtual functions we have defined earlier.

	cout << "Invoking a virtual function on each in turn" << endl;
	Incoming.FormattedName(cout);
	Outgoing.FormattedName(cout);  

We'll then print out the whole database and see what it looks like.

	cout << endl << "Dumping entire database" << theDB;
	  
	cout << "Test Completed" << endl;  
	return EXIT_SUCCESS;
} 

 

Feature index

(c) Copyright A.D. Software 1994-2000 (All Rights Reserved).
Last Updated: 9th September 2001