根据解析的字符串创建对象

Posted

技术标签:

【中文标题】根据解析的字符串创建对象【英文标题】:Creating object based on string parsed in 【发布时间】:2011-03-07 14:59:05 【问题描述】:

假设我有一个班级Image。在解析的某个时刻,“图像”在适当的时间被读入,这意味着我想创建一个 Image 类的对象。

我正在考虑将这些字符串映射到对适当类的构造函数调用,但我不确定如何实现这一点。

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

【问题讨论】:

我无法解析这个问题。 Is there a way to instantiate objects from a string holding their class name? 的可能重复项 【参考方案1】:

您正在描述一个工厂函数。有很多方法可以实现这一点,从注册表到简单的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 链接,直到您需要更复杂的东西。一旦无法在一个位置定义所有子类型,您通常会去注册。

【讨论】:

REGISTER_MEDIA 的定义中,#Image 是错字吗?应该是#type吧?【参考方案2】:

您不能存储指向构造函数的函数指针,但可以存储指向返回新构造对象的函数的指针,即

Image *createImage() 
    return new Image();

然后你可以在你的地图中存储一个指向这个函数的指针。

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

然后调用它

Image *myImage = constructorMap["Image"]();

【讨论】:

【参考方案3】:

我不是 100% 确定你在问什么,但我会猜测一下。

您可以将构造函数包装在函数中:

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

然后你可以在你的地图中存储函数指针!

map["Image"] = makeImage;

稍后再给他们打电话!

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

当然,这里的限制是函数的类型签名必须采用相同的类型参数,并且必须返回相同的类型(类的实例是 Image 或 Image 的父级)。

【讨论】:

【参考方案4】:

正如斯蒂芬指出的那样,您所描述的是工厂模式(假设 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));

【讨论】:

以上是关于根据解析的字符串创建对象的主要内容,如果未能解决你的问题,请参考以下文章

解析 JSON 文件(本地存储),从而快速创建对象数组

无法使用Circe JSON解析器创建遍历JSON字符串的对象

如何从 base64 编码字符串创建 Java Key 对象,以便使用 JJWT 进行 PS256 解析

mybatis源码解析4---SqlSession解析

根据字符串生成类---类的类型.self---根据字符串创建控制器对象

根据评论(对象)解析云代码生成列表