You are currently browsing the archives for the inheritance tag.

Variadic templates

August 7th, 2014

Introduction

In this article we’re going to look at a new feature of templates in C++11 – the concept of the variadic template.

Variadic templates allow us to create functions and classes, not only with generic types, but also a variable number of generic types.

Read more »

Template member functions

July 24th, 2014

Introduction

Previously we’ve looked at template functions and we’ve looked at template classes. This time, let’s look at what happens when you combine them.

Read more »

Template inheritance

June 19th, 2014

Introduction

Previously we looked at template class syntax and semantics. In this article we’ll extend this to look at inheritance of template classes.

Read more »

Inheritance, ABCs and Polymorphism

March 14th, 2011

Virtual functions

Virtual functions in C++ exist to maintain the consistent behaviour of polymorphism when accessing derived objects via base class pointers. (If that statement has made your head spin, I’d suggest reading this article before carrying on)

class Base
{
public:
  virtual void v_op();
};

class Derived : public Base
{
public:
  virtual void v_op();
}

I can access either a Base object or a Derived object via a Base pointer; and I should get the appropriate behaviour for the actual type of the object I’m pointed at:

Base* pB;
pB = new Base;	  // Point at Base object…
pB->v_op(); 	  // calls Base::v_op()
pB = new Derived  // Derived object is also a Base object
pB->v_op()	  // this time calls Derived::v_op()

This mechanism is known as dynamic polymorphism.
Dynamic polymorphism is a very powerful mechanism for building flexible, maintainable solutions. If we base the client code not on a particular object but on a more abstract (in the design sense) super class (something known as the Dependency Inversion Principle) we can swap implementations in and out without having to re-factor the client code (using a principle called Substitutability).

Abstract Base Classes

Let’s take a common design problem. During design we identify a set of classes with some similar behaviour but some unique behaviour. For example, our design may require concurrent behaviour so we define a set of ‘active’ classes, each one doing a different task:

class ActiveComms
{
public:
  void start();    // Create a thread; and call doStuff()
  void doStuff();  // Perform this class’ behaviour
};

class ActiveDisplay
{
public:
  void start();    // Create a thread; and call doStuff()
  void doStuff();  // Perform this class’ behaviour
};

class ActiveControl
{
public:
  void start();    // Create a thread; and call doStuff()
  void doStuff();  // Perform this class’ behaviour
};

These objects share common behaviour – they all have to create and manage a thread of control in the underlying OS – but they all have (at least one) unique behaviour – the actual work they are doing (for example, managing the display)
Rather than replicating the common code it is put into a base class and the ‘working’ classes inherit from it. We make the unique behaviour virtual so that derived classes can provide their own (overridden) implementation.

class Active
{
public:
  void start();            // Create a thread; ; and call doStuff()
  virtual void doStuff();  // What should this function do?
};

class ActiveComms : public Active
{
  virtual void doStuff();  // ActiveComm’s unique behaviour
};

In the client code we can use dynamic polymorphism to decouple us from any particular implementation:

Active* pActiveObject;
pActiveObject = new ActiveComms;   // Derived class
pActiveObject->start(); 	   // Assume start() calls
                                   // polymorphic doStuff()

What’s to stop us creating a base class object, though?

Active* pActiveObject;
pActiveObject = new Active;        // Base class object.  Correct, but…
pActiveObject->start(); 	   // what happens here?

What happens when the base class object runs? What does its doStuff() function do? At best it may do some generic (common) behaviour; at worst nothing at all.
In reality we don’t want clients to create objects of the common base type. They’re a convenience to improve intrinsic quality, not really part of the functional behaviour. In order to inhibit creation of base class objects we make the virtual function pure.

class Active
{
public:
  void start();                // As before
  virtual void doStuff() = 0;  // Pure virtual function.
};

Active is now referred to as an Abstract class. You cannot create an instance of an abstract class. This is because a pure virtual function does not require an implementation. (We’ll talk about what happens if you try and call this base class function in a while). Derived classes must implement the doStuff() function; they then become what are called Concrete classes.

// Active* pActiveObject = new Active     // Fails – Active is abstract
Active* pActiveObject = new ActiveComms;  // Concrete class
pActiveObject->start();

What happens if a derived class calls the base class’ (pure) virtual fuction? C++ scope operator (::) allows you to call a function outside your current scope; and in fact anywhere in the class hierarchy.

// Class declarations as before.
void ActiveComms::doStuff() 	// virtual function
{
  // Do ActiveComms unique behaviour…
  Active::doStuff();            // Call base class’ (pure) virtual function.
                                // This is legal, but what happens?!
}

What happens when you write code like this depends on your compiler. On most modern compilers – for example GCC, IAR or even Microsoft! – you will get a Linker error when you compile this code. This is because there is no implementation for the pure virtual function (as expected).
Be careful, though: on some older compilers declaring a function as a pure virtual may cause the compiler to insert a null (zero) into the vtable for the class. The code compiles and links with no errors or warnings. When the code executes and the pure virtual function is called (via the vtable) it will execute a null pointer. On many processors this is the reset vector; meaning your call to the base class pure virtual function will reset your system (with no warning!)

Pure Virtual Functions with Implementations

So, can you provide an implementation for a pure virtual function? And why would I want to?
In our example, let’s assume there is some common behaviour that all our concrete classes have to perform as part of their doStuff() function. It makes sense to collect the common behaviour together in the base class – but, we still don’t want clients to create instances of the base class, Active.
Remember, adding (at least one) pure virtual functions to a class makes it an abstract class, meaning you cannot create an instance of it. A pure virtual class is not required to have an implementation – but that doesn’t mean you can’t provide an implementation.
The solution to our problem is to keep the Active class exactly as before but add an implementation to its doStuff() pure virtual function that contains all the common behaviour. This common behaviour can be called from the overridden derived classes’ doStuff() function:

class Active
{
public:
  void start();                // As before
  virtual void doStuff() = 0;  // Pure virtual function.
};

void Active::doStuff()		// Pure virtual function implementation!
{
  // Common behaviour goes in here.
}

class ActiveComms : public Active
{
  virtual void doStuff();  // ActiveComm’s unique behaviour
};

void ActiveComms::doStuff() 	// virtual function
{
  // Call base class’ (pure) virtual function.
  // Contains all the common behaviour.
  //
  Active::doStuff();       

  // Do ActiveComm's unique behaviour…
}

The design and use of pure virtual functions is two-fold:

  • To create abstract classes which acts as a common interface (or contract) to client code. Derived concrete classes can be substituted for the abstract base class (using dynamic polymorphism). The abstract base class designer must specify the (only) set of services the client can expect.
  • To force derived classes to provide their own implementation of the function. The abstract base class designer must specify which parts of the implementation can be shared and which must be unique.

Use of abstract base classes and substitution, using pure virtual functions and dynamic polymorphism allows you to build flexible and adaptable solutions, particularly in areas of your system that will be subject to change over the life of the system.

The Baker’s Dozen of Use Cases

February 14th, 2011

Rule 12: Avoid variations on a theme

A common affliction amongst novice use case modellers (particularly those from a development background) is the desire to fettle the use case model – to organise it, revise it, balance it; and generally make it look more like a design model.  Unfortunately, beyond a certain point this effort actually starts to degrade the utility and effectiveness of the model.  More and more effort is put into a model that becomes less and less useful to the customer and the analyst.

This all-too-common situation is known as ‘Analysis Paralysis’.

I always advise, unless you can provide a really compelling reason to do otherwise avoid associations between use cases.  Yes, you might have a less optimal model, but at least your stakeholders will find it easy (or easier) to follow.  This is price you always pay with use case associations: Elegant but complicated to understand, versus sub-optimal but explicit.  In my view explicit always wins; after all, we’re trying to understand the problem, not design a solution.

Case in point:  Use case specialisation.

In UML a use case (and indeed an actor) is a special form of classifier. One of the properties of classifiers is that they can be specialised – that is, a new classifier may be defined that has the properties and behaviours of the parent, but may extend or modify these attributes. The definition of the specialised classifier is that it is also a the type of its parent – that is, all instances of the specialised classifier form a subset of the set of parent classifiers.

 

image

Use case and actor specialisation

In use case terms it means we can define actors or use cases that represent specialisations of some base behaviour.  As part of our analysis we may choose to define actors whose goals form a superset of another actor’s. For example, we could define an Administrator actor as a specialisation of a User actor. The Administrator performs all the functions of the User but has additional behaviours (admin tasks)

Actor specialisation is avoidable if you consider actors as roles. A role is just a set of competencies and behaviours that can be adopted by a person (when interacting with the system). So, when I perform everyday tasks I adopt the role of a User actor; when I need to do an administrative task I adopt the role of an Administrator (if I’m able) This model also means I can be an Administrator without being a User – something the specialisation model doesn’t allow.

For use cases the problem becomes more complex. Given any particular use case, what does it mean to specialise it?  A specialised use case is like a base use case, but may have different behaviour. This complication is adding nothing to our comprehension of the system (in fact, possibly the opposite!).  Not to mention the pain you will go through trying to explain all this to your customers!

The Liskow Substitutabiliy Principle (LSP) can help with this situation.  In simple terms the LSP says that a derived class can be substituted for a base class object if, and only if, the derived class has no stronger pre-conditions (demands no more of the client) and has no weaker post-conditions (delivers no less). 

For use cases, this means the specialised use case must have the same pre-conditions as the super use case (or possibly weaker).  The specialised use case may override super use case functionality (whatever that actually implies!).  Finally, none of the scenarios in the specialised use case must result in weaker post-conditions than the super use case. 

This could be achieved by having a super use case with limited (or no) behaviour, strong pre-conditions and very weak post-conditions.

However, one could reasonably argue this super use case becomes little more than a generic place-holder and doesn’t really add much to our understanding of the system’s (transactional) behaviour.

Use case specialisation can be avoided very simply by forking the use case with an optional flow. For one variation take one path through the use case; for another variation take the other. Unfortunately, this causes the use case to become more complicated; but at the benefit of explicitness.

There are some rare cases where use case specialisation can be more explicit but these are more the exception than the rule.

 

<<Prev     Next>>

%d bloggers like this: