Polymorphism
Polymorphism is about virtual functions, which allows for derived objects to change the structure and makeup of functions defined in base classes.
We can presume that our document management system supports the archiving of its various document classes. The base class already has placeholders for such functions as shown by the three base class member functions in Listing 7.
The Base Class Member Functions
virtual void StoreDocument() {};
virtual void GetDocument() {};
virtual void DeleteDocument() {};
The capability to bind code to these functions based on the type of the caller is an important aspect of polymorphism. Let's make this more concrete by assuming that invoices are stored in one content management system, and contracts are stored in another. The derived classes InvoiceDocument and ContractDocument must supply specific code to these member functions. Listing 8 shows the addition of the member function StoreDocument() to the ContractDocument class.
Addition to the Contract Document Class
class ContractDocument : AnyOldDocument {
public:
explicit ContractDocument(int Id, char* name, int docType, int number);
virtual ~ContractDocument();
const int getPenaltyClauseValue();
void setPenaltyClauseValue(int number);
virtual void StoreDocument(); // New addition
}
It is good practice to indicate that this is a virtual function; for example, if you wanted to later review your use of virtual functions. The latter might arise if you were looking at performance. So, how do we use this new function?
Calling the Store Document () function
Here it is shown the member function definition in Contract Document.
void ContractDocument::StoreDocument()
{
printf("Now storing contract to archive\n");
}
It just shows a simple printf() statement. In a real system, we would typically call into some external content management system to archive the contract. Similar considerations apply to archiving invoices.
Complete Program Output
It shows the complete program output.
Program Output
Invoking 3-parameter constructor for AnyOldDocument
Creating a Memo document for: Calling All Employees
Invoking 4-parameter constructor for AnyOldDocument
Creating an invoice document for: A Big Customer
Invoice price value 3500
Now storing invoice in content management system
Invoking 4-parameter constructor for AnyOldDocument
Creating a contract document for: An Organization
Contract penalty clause value 1000
Updated contract penalty clause value 50000
Now storing contract to archive
Invoking the destructor for AnyOldDocument
Invoking the destructor for AnyOldDocument
Invoking the destructor for AnyOldDocument
The first half of this Listing shows the instantiation of objects and initialization code. I also update the invoice price and contract penalty clause values. I then archive the documents prior to deleting them.
Make Your Destructors Virtual
As we saw earlier, making destructors virtual is a terrific way of ensuring that resource deallocation can occur. This is a feature of C++ that comes for free and is well worth using as a matter of course. Virtual destructors are automatically invoked, so there's no big effort required on the programmer's part. However, do remember that virtual functions aren't as efficient as their nonvirtual counterparts. So, if performance is a key requirement, you might like to carefully consider the use of virtual functions.
Conclusion
These base classes then serve many of the needs of the derived classes. Less code means less expense in maintenance and upgrades. I often wonder how much code is needlessly duplicated nowadays. As we've seen, the inheritance and polymorphism features supported by C++ can be leveraged to great advantage to produce very functional and yet quite generic base classes.
Before adding code to derived classes, see whether you can add it to the base class in some generalized form. It's entirely possible that you might be simply duplicating code unnecessarily by placing it in the derived classes. The same or similar placed in derived classes has to be maintained throughout the lifecycle. If it resides in the base class, only one version of the code has to be continued.
Polymorphism and virtual functions are inextricably interwoven. In many ways, polymorphism is close to magic! However, it does come with a price tag in terms of performance and very possibly ease of understanding. All the best!