2010-12-05 36 views
9

J'essaie de créer un modèle d'usine abstrait pour plusieurs usines abstraites en C++ et est venu avec cela.C++ Abstract Usine utilisant des modèles

#define _CRTDBG_MAP_ALLOC 
#include <crtdbg.h> 
#include <map> 
#include <stdio.h> 

class Base 
{ 
public: 
    virtual ~Base() {} 

    virtual bool Get() = 0; 
}; 

class DerivedA : public Base 
{ 
public: 
    bool Get() 
    { 
     return true; 
    } 
}; 

class DerivedB : public Base 
{ 
public: 
    bool Get() 
    { 
     return false; 
    } 
}; 

template <class T> 
class Creator 
{ 
public: 
    virtual ~Creator(){} 
    virtual T* Create() = 0; 
}; 

template <class T> 
class DerivedCreator : public Creator<T> 
{ 
public: 
    T* Create() 
    { 
     return new T; 
    } 
}; 

template <class T, class Key> 
class Factory 
{ 
public: 
    void Register(Key Id, Creator<T>* Fn) 
    { 
     FunctionMap[Id] = Fn; 
    } 

    T* Create(Key Id) 
    { 
     return FunctionMap[Id]->Create(); 
    } 

    ~Factory() 
    { 
     std::map<Key, Creator<T>*>::iterator i = FunctionMap.begin(); 
     while (i != FunctionMap.end()) 
     { 
      delete (*i).second; 
      ++i; 
     } 
    } 
private: 
    std::map<Key, Creator<T>*> FunctionMap; 
}; 

int main(int argc, char** argv[]) 
{ 
    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); 

    //Register 
    Factory<Base, char*> temp; 
    temp.Register("DA", (Creator<Base>*)new DerivedCreator<DerivedA>); 
    temp.Register("DB", (Creator<Base>*)new DerivedCreator<DerivedB>); 

    //Pointer to base interface 
    Base* pBase = 0; 

    //Create and call 
    pBase = temp.Create("DA"); 
    printf("DerivedA %u\n", pBase->Get()); 
    delete pBase; 

    //Create and call 
    pBase = temp.Create("DB"); 
    printf("DerivedB %u\n", pBase->Get()); 
    delete pBase; 

return 0; 
} 

Il compile et fonctionne très bien sans fuites de mémoire (win32 crtdbg) mais je ne sais pas si cela est vraiment la bonne façon de faire un modèle de fabrique abstraite.

temp.Register("DA", (Creator<Base>*)new DerivedCreator<DerivedA>); 

Je m'interroge également sur la ligne ci-dessus. Je suis confus pourquoi je dois lancer. Je ne comprends pas très bien les modèles, mais je suppose que cela devrait fonctionner correctement, étant donné que la classe template et la classe réelle sont dérivées.

Ce code fonctionne réellement bien comme indiqué ci-dessus et supprime même bien sans fuites de mémoire. Je ne me sens tout simplement pas à l'aise avec ça.

Je n'ai pas pu trouver de véritables exemples de classes de modèle, sauf pour ce billet depuis MaNGOS (wow émulateur) - https://mangos.svn.sourceforge.net/svnroot/mangos/trunk/src/framework/Dynamic/ObjectRegistry.h

Mais je ne pense pas que je peux utiliser cette méthode dans mon projet parce que je prévois sur l'utilisation des DLL à un moment donné dans mon projet et il utilise CRTP qui est contre mon exigence pour le polymorphisme d'exécution.

+0

Eh oui, la ligne que vous avez envoyé mauvais. Il n'y a pas de relation entre les deux types. Ils sont spécialisés pour différents types. Je ne sais pas non plus pourquoi vous vous occupez du CRTP. Habituellement, il est utilisé pour * éviter * les fonctions virtuelles. Mais vous en avez encore, alors pourquoi s'embêter avec les templates? – jalf

+0

Eh bien ce que j'essaie de faire est de créer une solution en 3 parties. Programme, bibliothèque et DLL. La DLL contiendra l'implémentation, la bibliothèque contient l'usine et le programme utilise l'interface. Le modèle existe parce que je vais le faire beaucoup. Je l'utilise pour remplacer la sélection du pilote de mon moteur de jeu actuel. Actuellement, il a copié/collé le code pour la vidéo, la physique, l'entrée et l'audio. – NtscCobalt

Répondre

10

La classe DerivedCreator<DerivedA> est un Creator<DerivedA> pas Creator<Base>.

Vous devez dire le modèle dérivé que soit le type de base pour qu'il puisse mettre en œuvre l'interface de Creator<Base> en créant une instance de type dérivé:

// DerivedCreator is Creator<BaseType> which creates a 
// DerivedType, not a Creator<DerivedType> 
template <class DerivedType, class BaseType> 
class DerivedCreator : public Creator<BaseType> 
{ 
public: 
    BaseType* Create() 
    { 
     return new DerivedType; 
    } 
}; 

// Register 
Factory<Base, std::string> temp; 
temp.Register("DA", new DerivedCreator<DerivedA, Base>); 
temp.Register("DB", new DerivedCreator<DerivedB, Base>); 

// or if you want to create lots with the same base: 
template <class DerivedType> 
class DerivedBaseCreator : public DerivedCreator<DerivedType, Base> {}; 

//Register 
Factory<Base, std::string> temp; 
temp.Register("DA", new DerivedBaseCreator<DerivedA>); 
temp.Register("DB", new DerivedBaseCreator<DerivedB>); 
+0

Ah merci je pense que c'est exactement ce que je cherchais. Pour ma part, je n'ai pas compris que "La classe DerivedCreator est un créateur pas un créateur .". – NtscCobalt

0

Remarques petites pour améliorer la conception: 1) Utiliser shared_ptr au lieu des pointeurs premières 2) utiliser std :: string au lieu de char *

Vous devez jeter, car les types Créateur, Créateur et créateur < DerivedB > sont des types complètement différents. La façon de résoudre ce problème est d'éliminer les moulages:

//Register 
Factory<Base, char*> temp; 
temp.Register("DA", new DerivedCreator<Base>); 
temp.Register("DB", new DerivedCreator<Base>); 
+2

Substituer aveuglément des pointeurs bruts à 'shared_ptr' est une très mauvaise idée. Non seulement vous risquez d'obtenir des références circulaires, et les fuites de mémoire associées, c'est aussi de loin le type le plus lent de pointeur intelligent. Utilisez des pointeurs intelligents, oui. Mais n'utilisez 'shared_ptr' que lorsque vous avez absolument besoin d'une propriété partagée. – jalf

+0

Oui, je suis tout à fait d'accord pour dire que s'éloigner de char * est une bonne idée. Trouvé celui sur la façon difficile quand j'ai appelé pBase = temp.Create ("DA"); à partir d'un espace d'adressage distinct de la fonction de registre. Je prévois d'utiliser des pointeurs intelligents, mais je suis mieux à la programmation simple sans eux, donc je vais les ajouter plus tard. – NtscCobalt

-1

Vous pouvez faire usine :: enregistrer une méthode de modèle et d'utiliser l'amplification mpl affirment l'intérieur

#include <boost/mpl/assert.hpp> 
#include <boost/type_traits.hpp> 

template <class T, class Key> 
class Factory 
{ 
public: 
/////////////////////////////////// 
template <typename _Base> 
void Register(Key Id, Creator<_Base> * Fn) 
{ 
    BOOST_MPL_ASSERT((boost::is_base_of<T, _Base>)); 

    FunctionMap[Id] = reinterpret_cast<Creator<T>*>(Fn); 
} 
/////////////////////////////////// 
//... 
}; 
+0

La seule chose que vous pouvez faire avec 'reinterpret_cast' est de renvoyer le résultat au type original, donc pour utiliser' FunctionMap', vous devez connaître les types spécifiques du créateur que vous obtenez, ce qui n'est pas une option viable . –

+0

Vous ne devez pas le renvoyer car l'affirmation garantit que _Base est dérivé de (ou est) le type T, le compilateur s'assure que Fn pointe vers le type Creator <"whatever">. – TomK