Instantiation dynamique en C++

Dans le cadre d’un petit projet, je me posais la question suivante: comment créer un objet facilement à partir du nom de sa classe. Par exemple, imaginons que j’aie une classe Product et deux classes qui héritent de Product: Product_child1 et Product_child2. On suppose de plus que ces deux classes ont les mêmes types de constructeur (par exemple un constructeur par défaut et un constructeur qui prend un entier comme paramètre). Mon but est de pouvoir faire quelque chose qui ressemble à cela:

Product *p1( instantiateObject( "Product_child1", 42 ) );
Product *p2( instantiateObject( "Product_child2", 58 ) );

Pour faire cela, on va passer par une classe ProductFactory qui va se charger de faire la création de l’objet selon le type souhaité. Cette classe va contenir un tableau associatif (map) avec une ligne pour chacun des types d’objets que l’on veut manipuler. Voici la classe en question:

class ProductFactory
{
	private:
		static map< string, Product *> productsTable;

	public:
		~ProductFactory( )
		{
			// libération de la mémoire
			map< string, Product *>::iterator itr;
			for( itr = productsTable.begin( ); itr != productsTable.end( ); ++ itr )
				delete itr->second;
		}

		void addProduct( string strClassName, Product *p )
		{
			ProductFactory::productsTable[ strClassName ] = p;
		}

		Product* instantiate( string strClassName, int iV )
		{
			if( productsTable.find( strClassName ) == productsTable.end( ) )
				return 0;

			return productsTable[ strClassName ]->create( iV );
		}
};

Pour pouvoir l’utiliser, notre main ressemble à cela:

int main() 
{
	// ------------------------------------------------------------------
	ProductFactory pf; // cet objet est utile tout au long du programe

	// on met une fois pour toute une instance de chaque classe
	pf.addProduct( "Product_child1", new Product_child1( ) );
	pf.addProduct( "Product_child2", new Product_child2( ) );

	// ------------------------------------------------------------------
	// création des objets
	Product *p1( pf.instantiate( "Product_child1", 42 ) );
	Product *p2( pf.instantiate( "Product_child2", 58 ) );

	// ------------------------------------------------------------------
	delete p1;
	delete p2;

    return 0; 
}

Et le code lié aux classes Produits:

class Product
{
protected:
	int iVal;

public:
	Product( int iValue )
		: iVal( iValue ) { }

	virtual ~Product( )
	{
	}

	virtual Product* create( int ) = 0;
	virtual void display( ) = 0;
};

class Product_child1 : public Product
{
public:
	Product_child1( ) 
		: Product( 0 )
	{
		cout << "Constructeur Product_child1 sans paramètre" << endl;
	}

	Product_child1( int iValue )
		: Product( iValue )
	{
		cout << "Constructeur Product_child1 avec paramètre: " << iValue << endl;
	}

	~Product_child1( )
	{
		cout << "Destructeur de Product_child1: " << iVal << endl;
	}

	Product_child1* create( int iV )
	{
		return new Product_child1( iV );
	}

	void display( )
	{
		cout << "display de Product_child1: " << iVal << endl;
	}
};

class Product_child2 : public Product
{
public:
	Product_child2( ) 
		: Product( 0 )
	{
		cout << "Constructeur Product_child2 sans paramètre" << endl;
	}

	Product_child2( int iValue ) 
		: Product( iValue )
	{
		cout << "Constructeur Product_child2 avec paramètre: " << iValue << endl;
	}

	~Product_child2( )
	{
		cout << "Destructeur de Product_child2 : " << iVal << endl;
	}

	Product_child2* create( int iV )
	{
		return new Product_child2( iV );
	}

	void display( )
	{
		cout << "display de Product_child2: " << iVal << endl;
	}
};

L’exécution du programme donne:

Constructeur Product_child1 sans paramètre
Constructeur Product_child2 sans paramètre
Constructeur Product_child1 avec paramètre: 42
Constructeur Product_child2 avec paramètre: 58
Destructeur de Product_child1: 42
Destructeur de Product_child2 : 58
Destructeur de Product_child1: 0
Destructeur de Product_child2 : 0

Voici le code complet:

#include <iostream>
#include <vector>
#include <map>
#include <string>

using namespace std;

class Product
{
protected:
	int iVal;

public:
	Product( int iValue )
		: iVal( iValue ) { }

	virtual ~Product( )
	{
	}

	virtual Product* create( int ) = 0;
	virtual void display( ) = 0;
};

class Product_child1 : public Product
{
public:
	Product_child1( ) 
		: Product( 0 )
	{
		cout << "Constructeur Product_child1 sans paramètre" << endl;
	}

	Product_child1( int iValue )
		: Product( iValue )
	{
		cout << "Constructeur Product_child1 avec paramètre: " << iValue << endl;
	}

	~Product_child1( )
	{
		cout << "Destructeur de Product_child1: " << iVal << endl;
	}

	Product_child1* create( int iV )
	{
		return new Product_child1( iV );
	}

	void display( )
	{
		cout << "display de Product_child1: " << iVal << endl;
	}
};

class Product_child2 : public Product
{
public:
	Product_child2( ) 
		: Product( 0 )
	{
		cout << "Constructeur Product_child1 sans paramètre" << endl;
	}

	Product_child2( int iValue ) 
		: Product( iValue )
	{
		cout << "Constructeur Product_child2 avec paramètre: " << iValue << endl;
	}

	~Product_child2( )
	{
		cout << "Destructeur de Product_child2 : " << iVal << endl;
	}

	Product_child2* create( int iV )
	{
		return new Product_child2( iV );
	}

	void display( )
	{
		cout << "display de Product_child2: " << iVal << endl;
	}
};

class ProductFactory
{
	private:
		static map< string, Product *> productsTable;

	public:
		~ProductFactory( )
		{
			// libération de la mémoire
			map< string, Product *>::iterator itr;
			for( itr = productsTable.begin( ); itr != productsTable.end( ); ++ itr )
				delete itr->second;
		}

		void addProduct( string strClassName, Product *p )
		{
			ProductFactory::productsTable[ strClassName ] = p;
		}

		Product* instantiate( string strClassName, int iV )
		{
			if( productsTable.find( strClassName ) == productsTable.end( ) )
				return 0;

			return productsTable[ strClassName ]->create( iV );
		}
};

map< string, Product *> ProductFactory::productsTable;

int main() 
{
	// ------------------------------------------------------------------
	ProductFactory pf; // cet objet est utile tout au long du programe

	// on met une fois pour toute une instance de chaque classe
	pf.addProduct( "Product_child1", new Product_child1( ) );
	pf.addProduct( "Product_child2", new Product_child2( ) );

	// ------------------------------------------------------------------
	// création des objets
	Product *p1( pf.instantiate( "Product_child1", 42 ) );
	Product *p2( pf.instantiate( "Product_child2", 58 ) );

	// ------------------------------------------------------------------
	delete p1;
	delete p2;

    return 0; 
}

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *