Я использую фабрику классов для динамического создания объектов. Я использовал это ответ за его простоту (и потому что я использую 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");
Я пытаюсь добавить дополнительный аргумент в фабрику классов для представления аргумента конструктора, но все мои попытки приводят к ошибке.
Зачем мне это нужно: простой случай:
создать новый объект — установить значения по умолчанию; для определенных объектов требуется диалог открытия файла, поскольку данные должны быть загружены из файла.
загрузить объект — заполняет данные, включая имя файла для объектов, которые содержат информацию о файле
Чтобы иметь возможность вызывать функцию «load», объект должен существовать — это означает, что, если я создаю новый объект, я вызову диалог открытия файла, даже если он мне не нужен.
Обход, который я вижу, состоит в том, чтобы иметь конструктор, сопровождаемый функцией установки. Но … это означает, что для создания объекта всегда требуется вызов двух функций, что выглядит как плохой дизайн.
Вот почему я ищу способ зарегистрироваться и вызвать классы с помощью простых вызовов, таких как
classFactory.registerType <Type1_Item> ("type1", bool);
Item* item = classFactory.create("type1", true);
Возможно ли это, и как я могу это сделать?
Вы можете использовать эту модифицированную версию
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);
Один из способов, который я могу придумать, заключается в том, именно так. Во-первых, мы собираемся хранить наши функции, используя 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);
@ Ответ Барри более чем полон. Однако, если вы просто заинтересованы в упрощенной фабрике, которая может конструировать объекты, конструкторы которых принимают разные параметры, вы можете сделать что-то вроде:
// 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
больше
Если все объекты имеют одинаковые типы параметров (здесь bool), просто измените функцию create следующим образом:
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);
Я сделал это с помощью пакетов параметров 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