3.16 Multiple Inheritance

C++ allows the use of multiple inheritance, where one class can have more than one base class. Remember back to the TransactionLodgement and Withdrawalclasses that we discussed previously. If we wished to create a FundsTransfer class, it would have properties of both a Lodgement and a Withdrawal. So the following structure could be devised, as in Figure 3.16, “Multiple Inheritance with the FundTransfer class.”.

Figure 3.16. Multiple Inheritance with the FundTransfer class.

Multiple Inheritance with the FundTransfer class.

This is a valid structure as a Lodgement IS A Transaction, a Withdrawal IS A Transaction and at different times during execution a FundTransfer IS A Lodgement and a Withdrawal.

Two separate Transaction objects are being created, and they do not interfere with each other (they have their own state). When using these Transaction objects you must be careful to resolve the ambiguity that results from having two objects of the same class. Suppose, a method was written in the FundTransfer class to calculate the time taken for the physical cash transfer to take place (the time between the withdrawal and the lodgement), you could use a code segment like:

 class FundTransfer: public Lodgement,
                     public Withdrawal
	  // states..
      virtual int getTimeTaken()
         return ( Lodgement::timeOfTransaction - 
                  Withdrawal::timeOfTransaction );

In this segment of code the multiple parents are comma separated and the getTimeTaken() returns the time taken as an int (could be milliseconds). In this method, the ambiguity is resolved between the two timeOfTransaction states that are inherited by the FundTransfer class, by specifying the parent that resulted in the inheritance of that state. So, Lodgement::timeOfTransaction is the timeOfTransaction state inherited through the Lodgement parent.

A common base class may not be inherited twice!

This same notation may be used to resolve ambiguity when referring to the class directly. If the Transaction had a display() method, and it was to be called directly, we could use:

  FundTransfer *fundPtr;
  fundPtr -> display(); 	        // not allowed - ambiguous
  fundPtr -> Lodgement::display();	// OK!
  fundPtr -> Withdrawal::display();	// OK!

This is awkward and does not give us an appropriate level of abstraction. It would be more appropriate to declare a new display() method in the FundTransfer class to avoid this ambiguity.

In the previous example it made sense to have two individual Transaction objects, each with its own state, otherwise it would not have been possible to calculate the time taken. This is not always the case. For example:

If the task was to create a special Current Account that had all the benefits of a Deposit account and all the benefits of a Current account, then a structure as shown in Figure 3.17, “Multiple Inheritance with the CashSave class.” could be devised. We could call this class CashSave (nothing to do with AIB at all! ;-)).

Figure 3.17. Multiple Inheritance with the CashSave class.

Multiple Inheritance with the CashSave class.

Since Account contains the balance state, and CashSave is a type of Account, we do not want the balance state to be duplicated - having two different balance states! To fix this the CashSave class should only have one instance of its common indirect base class. This can be achieved by declaring the base class to bevirtual.

  class Current: public virtual Account 
    // etc..

  class Deposit: public virtual Account {
	// etc..

  class CashSave: public virtual Current,
                  public virtual Deposit{
    // etc.. virtual is not required here!

In effect, we are declaring that if either the Current class or the Deposit class should be used in a multiple inheritance hierarchy, then they should share the same instance of the Account class with any other class that has virtual inheritance of Account.

So, this simple alteration to the parent results in a shared parent object. The only difficulty with this design is that the alteration must be made when the Current and Deposit classes are being designed. The programmer/designer must therefore be aware of child classes that are to be created in the future. This is not always possible as the marketing department may not have concieved of a "Cashsave" account until many years after the original design.

Now that we have an example of a virtual base class, remember to use a dynamic_cast in this case.

These notes are copyright Dr. Derek Molloy, School of Electronic Engineering, Dublin City University, Ireland 2013-present. Please contact him directly before reproducing any of the content in any way.