假设我有一堂课Image
。在解析的某个时刻,“Image”在适当的时间被读入,这意味着我想创建一个 class 的对象Image
。
我正在考虑将这些字符串映射到对适当类的构造函数调用,但我不确定如何实现这一点。
IE
container.push_back( some_map[stringParsedIn] ); // basic idea
正如斯蒂芬指出的那样,您所描述的是工厂模式(假设 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));
}
您正在描述工厂功能。有很多方法可以实现这一点,从注册表到简单的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
在您需要更复杂的东西之前,您可能会想要坚持使用链接。一旦无法在一个位置定义所有子类型,您通常会选择注册中心。
您不能存储指向构造函数的函数指针,但可以存储指向返回新构造对象的函数的指针,即
Image *createImage() {
return new Image();
}
然后,您可以在地图中存储指向此函数的指针。
std::map<std::string, Image *(*)()> constructorMap;
constructorMap.insert(std::pair<std::string, Image *(*)()>("Image", createImage));
然后用
Image *myImage = constructorMap["Image"]();
我不是 100% 确定你在问什么,但我会猜测一下。
您可以将构造函数包装在函数中:
Image* makeImage(ArgType arg) { return new Image(arg); }
然后你可以在你的地图中存储函数指针!
map["Image"] = makeImage;
以后给他们打电话!
SuperclassOfImage soup = map["Image"](arg);
当然,这里的限制是函数的类型签名必须采用相同的类型参数并且必须返回相同的类型(类的实例是 Image 或 Image 的父级)。