基于解析的字符串创建对象

时间:2010-06-24 03:00:11

标签: c++ parsing classname

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

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

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

4 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:0)

您正在描述工厂功能。从注册管理机构到简单的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链接,直到您需要更复杂的东西。一旦确定了所有子类型的单一位置,您通常会选择注册表。

答案 2 :(得分:0)

您不能将函数指针存储到构造函数中,但是您可以存储指向函数的指针,该函数返回一个新构造的对象,即

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 :(得分:0)

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

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

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

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

map["Image"] = makeImage;

稍后打电话给他们!

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

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