Through the years of maturing in software development, I have migrated through a series of technologies to solve various programming problems.
During the initial stages of my C++ usage, I used the tried and true run-time dynamic
polymorphism, mostly known as virtual methods through class inheritance. To answer the question of
when to use virtual destructors, Herb Sutter has an excellent article called
Virtuality.
For more virtual destructor information, Item 33 in Scott Meyer's More Effective C++ is helpful.
Inheritance - virtual Functions FAQ has more useful information.
Class inheritance and virtual functions closely couple classes. I wanted start some
decoupling, and do some event based coupling through C#-like events/delegates.
C++ doesn't have a similar concept built-in, but there are various libraries available
while supply a similar concept: Boost's slot/signal system, or the one I ended up using:
FastDelegates.
FastDelegates are supposed to be fast. And they do work well.
As I do some work on the Windows platform, I had been using the MFC classes for some GUI work.
As I got more into multi-threaded designs, MFC started to show it's significant short-comings
related to modular and mult-threaded designs. I came across the Windows Template Library (WTL)
as a nice, fast, light-weight windowing library.
The WTL introduced to me the concept of the Curiously Recurring Template Pattern (CRTP). WTL and
ATL make significant use of the CRTP pattern. A good introduction can be found at Wikipedia's entry for
Curiously Recurring Template Pattern.
CRTP has brought me back tight-coupling of classes, but as a consequence, it offers up the ability to
integrate a number of concepts together: slot/signals aka delegates, maintenance of strong typing,
simulated dynamic binding aka static polymorphism, and fast execution.
The close-at-hand references don't mention one other refinement (I wish I could find the
original source of this trick), and that is one of conditional static polymorphism. There is a way
to conditionally make the polymorphism call: if the derived class doesn't over-ride a method,
the calling code doesn't get compiled, it gets optimized away.
For example:
template <typename T>
class base {
void implementation( void ) {};
void interface( void ) {
if ( &base<T>::implementation != &T::implementation ) {
static_cast<T*>( this )->implementation();
}
}
};
class derived1: public base<derived> {
};
class derived2: public base<derived> {
void implementation( void ) {};
};
In this example of the CRTP, the base class has a default implementation of the interface.
In class derived1, as there is no implementation defined, no implementation gets called. In
class derived2, where there is an implementation defined, it is called.