2010-12-05 7 views
9

私は、C++で複数の抽象ファクトリの抽象ファクトリテンプレートを作成しようとしています。C++ Abstractテンプレートを使用している工場

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

これは、コンパイルしていないメモリリーク(のWin32 crtdbg)と罰金実行されますが、これは本当に抽象的な工場テンプレートを行うための正しい方法であるかどうかはわかりません。

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

私は上記の行についても不思議です。私はなぜキャストしなければならないのか混乱している。私は非常によくテンプレートを理解していないが、テンプレートクラスと実際のクラスの両方が派生していることを考えるとうまくいくはずだと思います。

実際には、コードは上記のように正常に動作し、メモリリークのない状態でも正常に削除されます。私はちょうどそれを完全に快適に感じることはありません。

私はマンゴー(すごいエミュレータ)からこれ以外テンプレートクラスの任意の実際の例を見つけることができていない

からhttps://mangos.svn.sourceforge.net/svnroot/mangos/trunk/src/framework/Dynamic/ObjectRegistry.h

しかし、私は私が計画しているので、私は私のプロジェクトで、そのメソッドを使用することができるとは思いません私のプロジェクトのある時点でDLLを使用すると、実行時の多態性に関する私の要求に反するCRTPが使用されます。

+0

うん、あなたが掲示ラインが悪い:

あなたは基本型は、それが派生型のインスタンスを作成することにより、Creator<Base>のインタフェースを実装することができますので、どのような派生テンプレートを伝える必要があります。 2つのタイプの間には関係がありません。彼らはさまざまなタイプに特化しています。私はCRTPをどうして迷っているのかも分かりません。通常は、仮想関数を避けるために使用されます。しかし、あなたはまだそれらを持っている、なぜテンプレートを気にする? – jalf

+0

私がしようとしているのは、3つの部分のソリューションを作成することです。プログラム、ライブラリ、およびDLL。 DLLには実装が含まれ、ライブラリにはファクトリが含まれ、プログラムではインターフェイスが使用されます。 私はこれをやっているので、テンプレートが存在します。私は現在のゲームエンジンのドライバ選択を置き換えるためにそれを使用しています。現在、ビデオ、物理、入力、およびオーディオ用のコピー/ペーストコードがあります。 – NtscCobalt

答えて

10

内のクラスDerivedCreator<DerivedA>を主張ブーストを使用するには、Creator<DerivedA>ないCreator<Base>です。

// 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

ああ、これは私が探していたものだと思う。私自身は理解できませんでした。クラスDerivedCreator は、作成者であり、作成者ではないです。 " – NtscCobalt

0

設計改善するための小型の発言: 1)の代わりに生のポインタ 2ののshared_ptrを使用)のstdを使用::代わりに文字の文字列*

なぜなら種類作成者、作成者や作成者< DerivedBあなたは、キャストする必要があり>は完全に異なるタイプです。それを修正する方法のキャストを削除することです:

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

'shared_ptr'のために生のポインタをブラインドで置き換えることはかなり悪い考えです。循環参照やそれに関連するメモリリークが発生する危険性があるだけでなく、最も遅い種類のスマートポインタでもあります。スマートポインタを使用してください。しかし、共有の所有権が絶対的に必要な場合にのみ、特に 'shared_ptr'を使用してください。 – jalf

+0

はい、char *から離れることは良い考えです。私が電話をしたときに難しい方法を見つけた。 pBase = temp.Create( "DA"); は、レジスタ機能とは別のアドレス空間からのものです。 スマートポインタの使用を計画していますが、後で追加するため、スマートポインタを使用せずに単純なプログラミングを行う方がよいです。 – NtscCobalt

-1

あなたが工場を作ることができます::テンプレートメソッドを登録し、MPL

#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

'reinterpret_cast'を使って移植可能なのは、結果を元の型にキャストするだけなので、' FunctionMap'を使うには、そこから得るクリエータの特定の型を知る必要があります。これは実行可能なオプションではありません。 –

+0

assertは_BaseがT型から派生していることを保証しているため、コンパイラはFnがCreator <"whatever">型を指していることを保証しますので、キャストする必要はありません。 – TomK

関連する問題