我可以为具有参数的构造函数创建类工厂吗?

时间:2015-05-11 18:06:30

标签: c++ class class-factory

我正在使用类工厂动态创建对象。我使用这个answer来简化(因为我使用的是Qt)。

但现在我意识到我必须在构造函数

中添加一个参数
Item(bool newItem /* = true*/);

而不是

Item();

参考答案中的代码:

template <typename T>
class ClassFactory
{
public:
    template <typename TDerived>
    void registerType(QString shape)
    {
        _createFuncs[shape] = &createFunc<TDerived>;
    }    
    T* create(QString shape)
    {
        typename QMap<QString, PCreateFunc>::const_iterator it = _createFuncs.find(shape);
        if (it != _createFuncs.end())
        {
            return it.value()();
        }
        return NULL;
    }    
private:
    template <typename TDerived>
    static T* createFunc()
    {
        return new TDerived();
    }
    typedef T* (*PCreateFunc)();
    QMap<QString, PCreateFunc> _createFuncs;
};

我注册了课程

classFactory.registerType <Type1_Item> ("type1");

需要时,我打电话给

Item* item = classFactory.create("type1");

我试图在类工厂中添加一个额外的参数来表示构造函数参数,但我的尝试都会导致错误。

为什么我需要它:简单的案例:

  • 创建一个新对象 - 设置默认值;对于某些对象,它需要一个打开的文件对话框,因为必须从文件加载数据。

  • 加载对象 - 填充数据,包括包含文件信息的对象的文件名

能够呼叫&#34;加载&#34;函数,一个对象必须存在 - 这意味着如果我创建一个新对象,即使我不需要它,我也会触发一个打开文件对话框。

我看到的工作是,有一个构造函数后跟一个setup函数。但是......这意味着构建一个对象总是需要一个2函数调用,这看起来像是糟糕的设计。

这就是为什么我正在寻找一种方法来使用像

这样的简单调用来注册和调用类
classFactory.registerType <Type1_Item> ("type1", bool);
Item* item = classFactory.create("type1", true);

有可能,我该怎么办?

5 个答案:

答案 0 :(得分:3)

我能想到的一种方法是要求参数与完全匹配。首先,我们将使用boost::any存储我们的函数。这是因为它们可能有不同的类型,因此我们需要一个异类容器:

QMap<QString, boost::any> _createFuncs;

我们的register函数将创建一个特定的函数指针来存储在所述any中:

template <typename TDerived, typename... T>
void registerType(QString shape)
{
    _createFuncs[shape] = &createFunc<TDerived, T...>;
}

其中createFunc现在需要额外的参数:

template <typename TDerived, typename... Args>
static T* createFunc(Args... args)
{
    return new TDerived(args...);
}

关键是我们在创作方面所做的事情。我们需要检查我们为特定类型存储的any是否是正确的类型:

template <typename... Args>
T* create(QString shape, Args... args)
{
    using FPtr = T*(*)(Args...);

    auto it = _createFuncs.find(shape);
    if (it != _createFuncs.end())
    {
        // ok, it points to some any. is it the right any?
        FPtr* fptr = boost::any_cast<FPtr>(&it.value());
        if (fptr) {
            return (*fptr)(args...);
        }

        // alternatively to the above, we can have createFunc
        // throw bad_any_cast if you pass the wrong arguments
        // which could be a loud, explicit failure rather than 
        // a silent one
        return boost::any_cast<FPtr>(it.value())(args...);
    }
    return nullptr;
}

这样就可以了:

classFactory.registerType<Item, bool>("type1");
                              ^^^^^^
                              arg list

Item* item = classFactory.create("type1", true);
Item* item2 = classFactory.create<bool>("type1", 1);

但这会失败,因为any需要bool,而不是int

Item* item3 = classFactory.create("type1", 1);

答案 1 :(得分:2)

@ Barry的答案不仅仅是完整的。但是,如果您只对可以构造具有不同参数的构造函数的对象的简化工厂感兴趣,则可以执行以下操作:

// Factory method for classes having constructors
// that take an arbitary number of parameters

#include <memory>

class Factory
{
public:
    template<typename T, typename... Params>
    static std::unique_ptr<T> create(Params... params)
    {
        return std::make_unique<T>(params...);
    }
};

struct Foo
{
    Foo(int) {};
};

struct Bar
{
    Bar(bool, double) {};
};

int main()
{
    std::shared_ptr<Foo> foo = Factory::create<Foo>(42);
    std::shared_ptr<Bar> bar = Factory::create<Bar>(true, 42.5);
}

请注意,我在这里使用了智能指针,因此您不再需要跟踪new/delete

答案 2 :(得分:1)

您可以使用此修改版本

template <typename T, typename ... Ts>
class ClassFactory
{
public:
    template <typename TDerived>
    void registerType(QString shape)
    {
        _createFuncs[shape] = &createFunc<TDerived>;
    }    
    T* create(QString shape, Ts... args)
    {
        typename QMap<QString, PCreateFunc>::const_iterator it = _createFuncs.find(shape);
        if (it != _createFuncs.end())
        {
            return it.value()(args...);
        }
        return nullptr;
    }    
private:
    template <typename TDerived>
    static T* createFunc(Ts... args)
    {
        return new TDerived(args);
    }
    typedef T* (*PCreateFunc)(Ts...);
    QMap<QString, PCreateFunc> _createFuncs;
};

并使用它

ClassFactory<Item, bool> classFactory;
classFactory.registerType <Type1_Item> ("type1");

Item* item = classFactory.create("type1", true);

答案 3 :(得分:0)

如果所有对象都具有相同的参数类型(这里是bool),只需更改创建函数,如下所示:

T* create(QString shape, bool param)   //modified
{
    typename QMap<QString, PCreateFunc>::const_iterator it = _createFuncs.find(shape);
    if (it != _createFuncs.end())
    {
        return it.value()(param);  //modified
    }
    return NULL;
}    

还要更改createFunc:

static T* createFunc(bool param)
{
    return new TDerived(param);
}
typedef T* (*PCreateFunc)(bool);

答案 4 :(得分:-1)

我使用C ++ 11参数包完成了这项工作:

// pack.cc
#include <utility>

template<class T, typename... P>
T * create(P&&... params)
{
    return new T(std::forward<P>(params)...);
}


class X
{
public:
    X() {}
};

class Y
{
public:
    Y(int) {}
};

int main()
{
    X * x = create<X>();
    Y * y = create<Y>(1);

    delete x;
    delete y;
}

编译此示例g++ -std=c++11 -o pack pack.cc