2010-12-05 15 views
9

Ich versuche, eine abstrakte Factory-Vorlage für mehrere abstrakte Fabriken in C++ zu erstellen und kam damit auf.C++ Abstract Factory mit Vorlagen

#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; 
} 

Es kompiliert und läuft ohne Speicherlecks (win32 crtdbg) in Ordnung, aber ich weiß nicht, ob dies wirklich der richtige Weg ist eine abstrakte Fabrik Vorlage zu tun.

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

Ich frage mich auch über die Zeile oben. Ich bin verwirrt, warum ich wirken muss. Ich verstehe Vorlagen nicht sehr gut, aber ich würde annehmen, dass es gut funktionieren sollte, wenn man bedenkt, dass sowohl die Template-Klasse als auch die tatsächliche Klasse abgeleitet sind.

Dieser Code funktioniert tatsächlich gut, wie oben gezeigt, und löscht sogar gut ohne Speicherverluste. Ich fühle mich einfach nicht wohl dabei.

ich nicht in der Lage gewesen, mit Ausnahme dieses von Mango keine realen Beispiele von Template-Klassen zu finden (wow Emulator) - https://mangos.svn.sourceforge.net/svnroot/mangos/trunk/src/framework/Dynamic/ObjectRegistry.h

Aber ich glaube nicht, dass ich diese Methode in meinem Projekt verwenden kann, weil ich plane, auf die Verwendung von DLLs an einem bestimmten Punkt in meinem Projekt und es verwendet CRTP, die gegen meine Anforderung für Laufzeitpolymorphismus ist.

+0

Yep, die Zeile geschrieben Sie ist schlecht. Es gibt keine Beziehung zwischen den beiden Arten. Sie sind auf verschiedene Arten spezialisiert. Ich bin mir auch nicht sicher, warum Sie sich überhaupt mit CRTP beschäftigen. Normalerweise wird es verwendet, um * virtuelle * Funktionen zu vermeiden. Aber Sie haben immer noch diese, also warum mit den Vorlagen kümmern? – jalf

+0

Nun, was ich versuche, ist eine 3-teilige Lösung zu erstellen. Programm, Bibliothek und DLL. Die DLL enthält die Implementierung, die Bibliothek enthält die Factory und das Programm verwendet die Schnittstelle. Die Vorlage existiert, weil ich das viel tun werde. Ich benutze es, um die Treiberauswahl meiner aktuellen Spiel-Engine zu ersetzen. Momentan gibt es einen Code zum Kopieren/Einfügen für Video, Physik, Eingabe und Audio. – NtscCobalt

Antwort

10

Die Klasse DerivedCreator<DerivedA> ist ein Creator<DerivedA> kein Creator<Base>.

Sie müssen der abgeleiteten Vorlage sagen, was die Typ-Basis, so dass es die Schnittstelle von Creator<Base>, indem eine Instanz des abgeleiteten Typs implementieren kann:

// 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 danke, ich denke das ist genau das was ich gesucht habe. Ich selbst habe das nicht verstanden "Die Klasse DerivedCreator ist ein Creator kein Creator .". – NtscCobalt

0

Kleine Bemerkungen das Design zu verbessern: 1) Verwendung shared_ptr statt rohen Zeiger 2) verwenden std :: string statt char *

Sie haben zu werfen, weil Typen Schöpfer, Schöpfer und Schöpfer < DerivedB > sind völlig verschiedene Arten. Die Art und Weise, dies zu beheben ist Abgüsse zu entfernen:

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

Blind ersetzen rohe Zeiger für 'shared_ptr' ist eine ziemlich schlechte Idee. Sie riskieren nicht nur zirkuläre Referenzen und die damit verbundenen Speicherverluste, es ist auch bei weitem die langsamste Art von Smart Pointer. Benutze intelligente Zeiger, ja. Verwenden Sie "shared_ptr" jedoch nur, wenn Sie unbedingt eine gemeinsame Eigentümerschaft benötigen. – jalf

+0

Ja, ich stimme definitiv zu, dass es eine gute Idee ist, sich von Char * wegzubewegen. Fand das auf die harte Tour, als ich rief pBase = temp.Create ("DA"); von einem separaten Adressraum als die Registerfunktion. Ich habe vor, intelligente Zeiger zu verwenden, aber ich bin besser darin einfache Programmierung ohne sie, also werde ich sie später hinzufügen. – NtscCobalt

-1

Sie Fabrik machen können :: eine Template-Methode registrieren und boost mpl behaupten innen

#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

Das einzige, was Sie portabel mit 'reininterpret_cast' machen können, ist, das Ergebnis auf den ursprünglichen Typ zurückzuspielen. Um' FunctionMap' zu verwenden, müssten Sie die spezifischen Typen des YouTubers kennen, was Sie nicht bekommen . –

+0

Sie müssen es nicht zurückwerfen, da assert garantiert, dass _Base vom Typ T abgeleitet ist (oder ist), der Compiler stellt sicher, dass Fn auf Creator <"whatever"> verweist. – TomK