如何在创建时自动注册类
How to automatically register a class on creation
我想知道是否存在自动注册类类型的设计模式或习语。或者更简单地说,我可以通过简单地扩展基类来强制调用类的方法吗?
例如,假设我有一个基类Animal
和扩展类Tiger
和Dog
,并且我有一个辅助函数,可以打印出所有扩展Animal
的类。
所以我可以有这样的东西:
struct AnimalManager
{
static std::vector<std::string> names;
static void registerAnimal(std::string name) {
//if not already registered
names.push_back(name); }
};
struct Animal
{
virtual std::string name() = 0;
void registerAnimal() { AnimalManager::registerAnimal(name()); }
};
struct Tiger : Animal
{
virtual std::string name() { return "Tiger"; }
};
所以基本上我会做:
Tiger t;
t.registerAnimal();
这也可以做成一个static
函数。是否有任何模式(例如奇怪的递归模板)或类似的东西可以帮助我实现这一点,而无需显式调用 registerAnimal
方法。
我希望我的class Animal
将来可以扩展,其他人可能会忘记打电话给register
,除了记录这一点之外,我还在寻找防止这种情况的方法(无论如何我都会这样做)。
PS 这只是一个例子,我实际上并没有实现动物。
您确实可以使用奇怪的递归模板习惯用法来做到这一点。它不需要任何扩展编译器无法强制执行的类的人:
template<class T>
struct Animal
{
Animal()
{
reg; //force specialization
}
virtual std::string name() = 0;
static bool reg;
static bool init()
{
T t;
AnimalManager::registerAnimal(t.name());
return true;
}
};
template<class T>
bool Animal<T>::reg = Animal<T>::init();
struct Tiger : Animal<Tiger>
{
virtual std::string name() { return "Tiger"; }
};
在此代码中,只有专用化Animal
才能扩展它。构造函数强制初始化static
成员reg
,进而调用 register 方法。
编辑:正如@David Hammen在评论中指出的那样,您将无法拥有Animal
对象的集合。但是,这可以通过拥有一个非模板类轻松解决,模板从该类继承并将其用作基类,并且仅使用模板进行扩展。
如果你坚持每个动物都应该注册,为什么不把name
Animal
构造函数的参数。然后,您可以将寄存器问题交给构造函数Animal
每个派生都必须传递有效的名称和寄存器:
struct Animal
{
Animal(std::string name){ AnimalManager::registerAnimal(name);}
}
struct Tiger : Animal
{
Tiger():Animal("Tiger"){}
};
这是一个典型的示例,您希望在构造对象时执行某种簿记。斯科特·迈耶斯(Scott Meyers)"有效C++"中的第9项给出了一个例子。
基本上,您将所有簿记内容移动到基类中。 派生类显式构造基类并传递基类构造所需的信息。例如:
struct Animal
{
Animal(std::string animal_type)
{
AnimalManager::registerAnimal(animal_type);
};
};
struct Dog : public Animal
{
Dog() : Animal(animal_type()) {};
private:
static std::string animal_type()
{
return "Dog";
};
};
通常我用宏来做这件事。
单元测试框架通常采用注册测试的技术,例如GoogleTest
@AMCoder:这不是你想要的答案。你想要的答案,反射(例如,what_am_I()
)实际上并不存在C++。
C++通过RTTI的形式相当有限。在基类中使用 RTTI 来确定正在构造的对象的"true"类型是行不通的。在基类中调用typeid(*this)
将改为为您提供正在构造的类的typeinfo
。
可以使类Animal
仅具有非默认构造函数。这对于直接从 Animal
派生的类工作正常,但是从派生类派生的类呢?此方法要么排除进一步继承,要么要求类生成器创建多个构造函数,其中一个构造函数供派生类使用。
您可以使用 Luchian 的 CRTP 解决方案,但这也存在继承问题,并且它还阻止您拥有指向Animal
对象的指针集合。将该非模板基类添加回组合中,以便您可以拥有 Animal
的集合,并且您再次遇到原始问题。您的文档必须说仅使用该模板来创建一个派生自Animal
的类。如果有人不这样做会怎样?
最简单的解决方案是你不喜欢的解决方案:要求派生自Animal
类的每个构造函数都必须调用register_animal()
。在您的文档中这样说。向代码的用户展示一些示例。在示例代码前面添加注释,// Every constructor must call register_animal().
使用您的代码的人无论如何都会使用剪切和粘贴,因此手头有一些可剪切和粘贴的解决方案。
担心如果人们不阅读您的文档会发生什么是过早优化的情况。要求每个类调用register_animal()
在其构造函数中是一项简单的要求。每个人都可以理解它,每个人都可以轻松实现它。如果您的用户甚至不能遵循这个简单的说明,那么您与用户之间的麻烦比注册失败要大得多。
您可以在基类构造函数中调用一个方法,每次实例化派生类时都会调用该方法,如下所示:
class Animal {
public:
Animal() {doStuff();}
}
doStuff() 方法可以在基类中实现以执行静态操作,也可以是纯虚拟的并在派生类中实现。
编辑:正如注释中正确指出的那样,虚拟方法无法在ctor中调用。
请注意,基类构造函数将在派生构造函数之前调用,因此您也可以执行以下操作:
class Animal {
public:
Animal(const std::string &name) {doStuff(name);}
private:
Animal(); // Now nobody can call it, no need to implement
}
class Dog : public Animal {
Dog() : Animal("Dog") {}
}
希望有帮助
- 用于创建/注册虚拟存储设备的 IOKit 驱动程序
- 如何在C++非托管/本机 DLL 中从注册免费 COM C# 托管 DLL 创建 COM 对象
- 注册表-获取值(而不是键)上次更改的时间和键创建日期
- 张量流错误:执行器无法创建内核。没有注册'Snapshot' 适用于 GPU 设备的 OpKernel 运行图像标签示例
- 正在创建与注册表同名的文件
- 如何挂钩新创建的注册表
- 为什么创建注册关键字
- 创建注册表密钥 - 拒绝访问
- 在结构中的某个点之后无法创建新注册表
- Qt在创建c++注册的QAbstractListModels后关闭时崩溃
- QML QQuickImageProvider 创建和注册
- 如何在 Visual Basic DLL 和 C++ DLL 之间创建隔离/无注册的 COM
- 如何在创建时自动注册类
- 训练sapi:创建转录的wav文件并将文件路径添加到注册表
- TCL C API创建并注册新频道
- C++逻辑错误:试图为创建的所有实例生成唯一的ObjectID,计数器注册错误
- 在注册表项中创建新数据
- 无法为文件创建注册信息
- 创建一个注册回调的抽象基类的正确方法
- c++不能在注册表中创建新键