Создание объекта на основе строки, проанализированной в - PullRequest
3 голосов
/ 24 июня 2010

Предположим, у меня есть класс Image. В какой-то момент синтаксического анализа «Image» читается в соответствующее время, что означает, что я хочу создать объект класса Image.

Что я рассматриваю, так это сопоставление этих строк с вызовом конструктора для соответствующего класса, но я не уверен, как это сделать.

1007 * Т.е. *

container.push_back( some_map[stringParsedIn] ); // basic idea

Ответы [ 4 ]

2 голосов
/ 24 июня 2010

Как отметил Стивен, вы описываете шаблон Фабрики (предполагая, что Image является абстрактным базовым классом). Однако для его реализации может быть полезно связать строки с функциями создания, как вы описали, вместо большой функции, состоящей из операторов if / else. Вот один из способов сделать это (при условии, что все ваши подклассы изображений могут быть сконструированы одинаково):

typedef Image* create_image_function();

template <class T>
Image* create_image(SomeType arg)
{
    return new T(arg);
}

...
map<string, create_image_function*> creators;
creators["Foo"] = &create_image<Foo>;
creators["Bar"] = &create_image<Bar>;
creators["Baz"] = &create_image<Baz>;

shared_ptr<Image> ImageFactory::make_image(const string& str)
{
    // checking to see if str exists as a key 
    // would be nice
    return shared_ptr<Image>(creators[str](arg));
}
0 голосов
/ 24 июня 2010

Вы не можете хранить указатели функций на конструкторы, но вы можете сохранить указатель на функцию, которая возвращает вновь созданный объект, т.е.

Image *createImage() {
    return new Image();
}

Затем вы можете сохранить указатель на эту функцию в вашемкарта.

std::map<std::string, Image *(*)()> constructorMap;
constructorMap.insert(std::pair<std::string, Image *(*)()>("Image", createImage));

А затем назовите его с помощью

Image *myImage = constructorMap["Image"]();
0 голосов
/ 24 июня 2010

Я не на 100% уверен в том, что вы спрашиваете, но я дам вам предположение.

Вы можете заключить конструкторы в функции:

Image* makeImage(ArgType arg) { return new Image(arg); }

И тогда вы можетесохраняйте указатели функций на вашей карте!

map["Image"] = makeImage;

Позже их вызовите!

SuperclassOfImage soup = map["Image"](arg);

Конечно, ограничение здесь состоит в том, что сигнатуры типов функций должны принимать один и тот же аргумент типа идолжен возвращать тот же тип (экземпляр класса, который является либо Image, либо родителем Image).

0 голосов
/ 24 июня 2010

Вы описываете заводскую функцию.Есть много способов сделать это, от реестров до простых цепочек if/else.

Обычно ваш класс Image является производным от аналогичного базового класса для других типов, анализируемых в.Таким образом, вы можете добавить их все в один контейнер.

Представьте себе эту иерархию:

class Media {
 public:
  virtual Save() = 0;
};

class Image : public Media {
 public:
   Image() { }
   virtual Save() { ... }
};

class Sound : public Media {
 public:
   Sound() { }
   virtual Save() { ... }
};

Самая простая конструкция - это фабричная функция:

Media *CreateMedia(const string &type) {
  if (type == "Image") {
    return new Image;
  } else if (type == "Sound") {
    return new Sound;
  } else {
    // handle invalid type error
  }
}

Другойальтернатива - использовать реестр, вместо CreateMedia вы обычно используете макрос, фабрику / реестр и какой-то механизм для создания своих подклассов:

// This is some mechanism to create types of Media.
template <typename T>
struct CreatorFunction {
  Media *operator() {
    return new T;
  }
};

// This is the factory that the types will register with.
class Factory {
 public:
  // singleton access function.
  static Factory* Get() {
    static Factory* f = new Factory; 
    return f;
  }

  // Creates Media of the given type.
  Media* Create(const string& name) { return registry_[name](); }

  // Records 'name' with the creator function 'func'.
  void Add(const string& name, const CreatorFunction &func) {
    registry_.insert(name, func);
  }
 private:
  Factory() { } // users can't create factories, they can only use singleton.
  hash_map<string, CreatorFunction> registry_;
};

#define REGISTER_MEDIA(type) Factory::Get()->Add(#type, CreatorFunction<type>);

REGISTER_MEDIA(Image);  // usually goes with the Image class.
REGISTER_MEDIA(Sound);  // usually goes with the Sound class.

int main(int argc, char** argv) {
  string parsedIn = "Image";
  Factory::Get()->Create(parsedIn);
}

В целом это более чистый подход, но вы можетевозникли проблемы с вашим компоновщиком, который думает, что некоторые символы не используются, и удаляет важные зарегистрированные классы из вашего двоичного файла.Возможно, вы захотите придерживаться цепочки if/then, пока вам не понадобится что-то более сложное.Обычно вы обращаетесь к реестрам, когда невозможно иметь единственное место, где определены все подтипы.

...