我正在使用 C++ 11。我有 N 个源文件,每个源文件都包含一个从公共基类继承的类。在启动时,我希望每个类都将自己注册到一个集合中。注册将包括 1) 识别类目的所需的数据,2) 用于创建类实例的类工厂。源文件的数量未知。这样做的模式是什么?该解决方案需要与 Visual Studio 2013、gcc 等跨平台兼容。
3 回答
首先要做的事情:vector
请记住,这只有在类派生自单个基类时才有可能,因为每个实例只能存储一种类型的对象。
当涉及到解决方案时......您可以在相应的 *.cpp 文件中声明一个对象(在 *.h 文件中将其标记为 extern):
// SomeClass.h
// <-- class declaration goes here
extern SomeClass someObj;
// SomeClass.cpp
SomeClass someObj;
并将其添加到构造函数内的向量中:
SomeClass(){
myVector.push_back(*this);
}
请注意,myVector
需要在此范围内可见。
在您myVector
包含一个SomeClass.h
带有SomeClass
. 请注意,如果填充是在基类中完成的,则不必在每个后续子类中都进行,因为无论如何都会调用基构造函数。
听起来像观察者模式。根据您想对这个类列表做什么,可能会有更好的模式。
交叉兼容性将完全取决于您的实现,但不应存在问题。
使用@Pawel 的建议,我创建了一个示例。目标是创建一个收集类列表的注册过程,每个注册都有一个类工厂和任意数据。具体来说,此示例将 Mime 类注册到 std::map 中。注册条目包含内容类型字符串的向量和用于创建 Mime 类的类工厂。
在每个源文件中,我使用一个单行来注册类。传递内容类型的向量以指定类支持的类型。每个类都继承自一个名为 Mime 的基类(未显示)。
RegisterTypes<Swagger> swagger(vector<const char *>({ "application/swagger+json" }));
在头文件中,定义一个结构以包含注册条目。需要两个初始化器,一个内容类型向量和类工厂(实现为 lamda)。
struct Registry
{
public:
Registry() {} // initializer below forces this initializer to be needed
Registry(vector<const char *> ct, function<Mime*(void)> cf) : contentTypes(ct), classFactory(cf) {}
vector<const char *> contentTypes;
function<Mime*(void)> classFactory;
};
在头文件中,外部包含注册的 std::map。每个映射条目由一个键和一个注册结构组成。实际定义在 .cpp 文件中(未显示)。注册表使用结构,因此它可以保存多个值。
extern std::map<const string, struct Registry> Mimes;
在头文件中,定义注册类。注册类的每个实例化都会在 std::map 中创建一个注册条目。在此示例中,注册类创建了一个注册条目,该条目由一个键(类名)、一个支持的内容类型的向量和一个类工厂组成。类工厂创建类的实例并作为 lamda 实现。每个注册的类都有一个通用的 Mime 基类。
template<class T> class RegisterTypes
{
public:
RegisterTypes(vector<const char *> contentTypes)
{
Mimes[typeid(T).name()] = { contentTypes, [](void) -> Mime * { return (Mime *)(new T()); } }; // requires a matching explicit initializer
}
};