[SNIP]
ObtainA() could return A pointer pointing either to B, or C, or D, or E.
How to code this so to tell Create() return B, or C, or D, or E at run-time?
Thanks!
As Bob already pointed out there are different ways to tackle this problem,
but run-time specific object creation is commonly solved by a factory. This
means that in principle you´ve got a number of classes derived from a common
base which all have a Create function. This function for the different
classes is registered with a factory and if you want to create an object of
a specific type you simply call up the factory with a specifier (e.g. class
name or whatever) which will return the appropriate object. This is a very
generic pattern and hence can be implemented via templates. For example:
// The factory
// simplefactory.h
#ifndef SIMPLEFACTORY_H
#define SIMPLEFACTORY_H
#include <string>
#include <map>
#include <sstream>
template<class TBase, class TIdType = std::string >
class CSimpleFactory
{
public:
typedef TBase* (*CreateCallback)(); // create callback functinos
// register with creator map
bool Register( const TIdType& Id, CreateCallback CreateFcn) {
return m_Creators.insert( std::map< TIdType,
CreateCallback>::value_type( Id, CreateFcn) ).second;
};
// unregister creator
bool Unregister( const TIdType& Id ) {
return m_Creators.erase( Id ) == 1;
};
// here the magic is performed
TBase* Create( const TIdType& Id ) {
// search for creator function
std::map< TIdType, CreateCallback>::const_iterator Iter =
m_Creators.find( Id );
if( Iter != m_Creators.end() ) {
return (Iter->second)();
}
else {
std::string Msg( "Creator function not registered for ==> " );
Msg += ToString( Id );
throw std::runtime_error( Msg );
}
return NULL;
};
private:
std::string ToString( const TIdType& Id ) {
std::istringstream is( Id );
return is.str();
};
private:
std::map< TIdType, CreateCallback> m_Creators;
};
#endif
///////////////// sample code ////////////////////
#include <iostream>
using namespace std;
class CBaseClass {
public:
virtual ~CBaseClass() {};
};
class CSquare : public CBaseClass {
// protect constructor so that the object can be created using
// the factory method only!
CSquare() {}
public:
void draw() { cout << "Square::draw\n"; }
void erase() { cout << "Square::erase\n"; }
virtual ~CSquare() { cout << "Square::~Square\n"; }
// NOTE: this function must be implemented for factory creation!!!
static CBaseClass* Create() { return new CSquare; };
};
int main(int argc, char* argv[])
{
CSimpleFactory<CBaseClass> Factory;
Factory.Register( "CSquare", &CSquare::Create );
// NOTE: If one only uses virtual functions then there is no need
// for this cast!
CSquare* pSquare= static_cast<CSquare*>( Factory.Create( "CSquare2" ) );
pSquare->draw();
delete pSquare;
return 0;
}
There are of course more elegant and sophisticated solutions implementing a
factory but this very simple one is sufficient most of the time.
HTH
Chris