Возникает вопрос:
Моего вопрос теперь, как я могу создать объект из строки в текстового файла, особенно не зная все производные классы.
Ответ: вы должны знать все производные классы.
C++ does not have reflection. Поскольку такие все имена классов связаны во время компиляции, и этот вид завода не имеет никакого выбора, кроме как сделать некоторые изменения:
if (name == "box")
return new Box();
else if (name == "circle")
return new Circle();
// ... etc ... etc ...
Существуют различные подходы и шаблоны проектирования, которые позволяют автоматизировать некоторые из эта работа работает и делает ее достаточно гибкой, чтобы не было явно поддерживать жесткий список всех подклассов.
Я просто опишу короткий, очень короткий подход. Очень простой, который я использовал раньше, и добивается, в значительной степени, того же результата: фабрика, которая может создавать экземпляр данного подкласса по имени, таким образом, что вам не нужно вручную редактировать завод и добавлять еще несколько строк кода. Весь процесс создания фабрики для нового подкласса может быть аккуратно завершен в процесс создания нового подкласса, что делает его довольно пуленепробиваемым, разделенным решением.
Рассмотрим простой механизм регистрации завод для этих подклассов:
typedef Shape (*shape_factory_t)();
Shape
ваш суперкласс формы.
Завод будет работать что-то вроде этого:
std::map<std::string, shape_factory_t> all_factories;
void register_factory(const std::string &name, shape_factory_t factory)
{
all_factories[name]=factory;
}
Так что теперь у вас есть карта всех ваших заводов. Вместо бесконечного if
заявления у вас есть одна карта, которую вы можете посмотреть по имени класса, и вызвать соответствующую фабрику, что-то вроде:
auto iter=all_factories.find(name);
if (iter == all_factories.end())
throw; // Some exception, unknown subclass
return (*iter->second)();
Все права, заботятся, что часть о. Теперь возникает вопрос: как зарегистрировать завод для каждого подкласса.
Допустим, у вас есть реализация Круга:
class Circle : public Shape {
class initializer;
// ... other things that make up the Circle
};
Затем в circle.cpp
, который реализует этот подкласс:
static Shape *create_circle()
{
return new Circle(); // Add constructor parameters, as appropriate
}
class Circle::initializer {
public:
initializer() {
register_factory("circle", create_circle);
}
};
static initializer initialize_me;
Таким образом, в Circle
регистры класса сам с завода, который создает экземпляр заданного Shape
по имени класса. Вы можете продолжить и реализовать все остальные подклассы индивидуально, не касаясь основного заводского кода. Вы можете объявить свой подкласс Box
таким же образом и зарегистрировать его на заводе, который затем автоматически узнает, чтобы создать класс Box
(предположительно, вызывая функцию create_box
()), учитывая имя «поле».
Есть еще одна деталь, о которой нужно позаботиться: порядок инициализации. Как вы знаете, относительный порядок инициализации объектов с глобальным охватом в разных единицах перевода является реализованной реализацией и в противном случае не указан C++.
Глобальные std::map
всех заводских функций должны быть сконструированы до того, как все подклассы попытаются зарегистрироваться и отправиться на карту при запуске приложения.
Это довольно типичный вопрос static initialization order fiasco, для которого существует несколько известных решений. Здесь объясняется, что in this answer должен работать нормально.
Google завод шаблон. – drescherjm