C ++ Abstract Factory с использованием шаблонов - PullRequest
11 голосов
/ 05 декабря 2010

Я пытаюсь создать шаблон абстрактной фабрики для нескольких абстрактных фабрик в C ++ и придумал это.

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

Меня также интересует строка выше.Я смущен, почему я должен сыграть.Я не очень хорошо понимаю шаблоны, но я предполагаю, что они должны работать нормально, учитывая, что и класс шаблона, и фактический класс являются производными.

Этот код работает нормально, как показано выше, и даже удаляется без утечек памяти.Мне просто не очень комфортно с этим.

Мне не удалось найти реальных примеров шаблонных классов, кроме этого, из MaNGOS (эмулятор wow) - https://mangos.svn.sourceforge.net/svnroot/mangos/trunk/src/framework/Dynamic/ObjectRegistry.h

Но я не думаю, что смогу использовать этот метод в своем проекте, потому что планирую использовать библиотеки DLL в какой-то момент в моем проекте, и он использует CRTP, что противоречит моим требованиям к полиморфизму во время выполнения.

Ответы [ 3 ]

12 голосов
/ 05 декабря 2010

Класс DerivedCreator<DerivedA> является Creator<DerivedA>, а не Creator<Base>.

Вам нужно сообщить производному шаблону, какой базовый тип, чтобы он мог реализовать интерфейс 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 голосов
/ 05 декабря 2010

Вы можете сделать Factory :: Register методом шаблона и использовать boost mpl assert внутри

#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 голосов
/ 05 декабря 2010

Небольшие замечания для улучшения дизайна: 1) Используйте shared_ptr вместо необработанных указателей 2) используйте std :: string вместо char *

Вы должны разыграть, потому что типы Creator, Creator и Creator это совершенно разные типы.Способ исправить это, чтобы удалить броски:

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